Canvas同源页面多画布,事件分发隔离

需求产生背景

当我们日常编写图像操作编辑器代码时,可能会碰到一个需求,需要在一个网页里面同时显示多个画布,并且要做到鼠标聚焦到一个画布之上的时候,我们可以操作这个画布之上的元素,同时多画布存在的这个网页中的其他画布【同源页面多画布】,要取消事件操作带来的影响【事件分发隔离】。

实现效果

核心逻辑

为了在一个页面中,能够展示多个绘制图像的窗口,这些窗口我们使用canvas进行分别绘制。然后在不同的canvas中展示不同的图像,为了更加方便的编写代码操作这些canvas画布。我们引入了PaperJS工具。

PaperJS是一个能够非常方便的操作Canvas的JavaScript工具。在PaperJS中,用户能够非常方便的创建路径,注册多个事件工具。管理Canvas画布的多个图层。并且能够管理同一个页面中的多个Canvas画布。

paperJS官网链接

逻辑步骤整理

  1. 创建一个HTML页面,在HTML页面中创建多个画布。
  2. 将这些画布中的paperScope,paperProject,paperView,paperTool分别做状态管理保存下来。
  3. 当画布得到聚焦的时候,我们后续对于这个画布的操作影响到其他画布。

代码实现

首先创建一个Vue3+Vite项目,语言选择TypeScript.

缓存数据结构设计

typescript 复制代码
import {ref} from 'vue'

export const AppStore = ref({

    canvas1: {
        canvasPaperScope: null as any,
        canvasPaperProject: null as any,
        canvasPaperView: null as any,
    },
    canvas2: {
        canvasPaperScope: null as any,
        canvasPaperProject: null as any,
        canvasPaperView: null as any
    }
})

paperjs画布挂载

typescript 复制代码
export function registerCanvas(canvas: string) {
    AppStore.value[canvas].canvasPaperScope = new paper.PaperScope().setup(canvas)
    AppStore.value[canvas].canvasPaperProject = AppStore.value[canvas].canvasPaperScope.project
    AppStore.value[canvas].canvasPaperView = AppStore.value[canvas].canvasPaperScope.view
    // 闭包内部注册辅助工具
    const horizen = new paper.Path.Line({
        from: new paper.Point(0, 0),
        to: new paper.Point(0, 0),
        strokeColor: canvas == 'canvas1'?'grey':'red',
        dashArray: [5, 5]
    })
    const vertical = new paper.Path.Line({
        from: new paper.Point(0, 0),
        to: new paper.Point(0, 0),
        strokeColor: canvas == 'canvas1'?'grey':'red',
        dashArray: [5, 5]
    })
    const ruleLineText = new paper.PointText({
        content: '(1,1)',
        strokeColor: canvas == 'canvas1'?'grey':'red',
        fontSize: 18,
        point: new paper.Point(0, 0)
    })
    // 注册工具
    const tool = new paper.Tool()
    tool.onMouseMove = (event: paper.MouseEvent) => {
        console.log(`画布${canvas}的移动`)
        horizen.visible = true
        vertical.visible = true
        ruleLineText.visible = true
        const point = event.point
        // 改变水平线位置
        vertical.segments[0].point = new paper.Point(0, point.y)
        vertical.segments[1].point = new paper.Point(AppStore.value[canvas].canvasPaperView.size.width, point.y)
        // 改变垂直线的位置
        horizen.segments[0].point = new paper.Point(point.x, 0)
        horizen.segments[1].point = new paper.Point(point.x, AppStore.value[canvas].canvasPaperView.size.height)
        ruleLineText.content = `(${Math.ceil(point.x)},${Math.ceil(point.y)})`
        ruleLineText.point = point
    }
    tool.activate()
}

完整源码

vue 复制代码
<template>
  <div class="app-container">
    <div class="item">
      <canvas id="canvas1" ref="canvas1" resize></canvas>
    </div>
    <div class="item">
      <canvas id="canvas2" ref="canvas2" resize></canvas>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { registerCanvas } from './utils/index'
import { AppStore } from './store/index'

let currentSelect = ''


const canvas1 = ref<any>()

const canvas2 = ref<any>()

onMounted(() => {
  loadData()
})

const loadData = async () => {
  await registerCanvas('canvas1')
  await registerCanvas('canvas2')
}

</script>
<style lang="scss" scoped>
.app-container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;

  .item {
    width: 50%;
    height: 100%;

    #canvas1[resize] {
      width: 100%;
      height: 100%;

      &:hover {
        border: 1px solid red;
      }
    }

    #canvas2[resize] {
      width: 100%;
      height: 100%;
      &:hover {
        border: 1px solid red;
      }
    }
  }

}
</style>
相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95271 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大1 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师1 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学1 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端