需求产生背景
当我们日常编写图像操作编辑器代码时,可能会碰到一个需求,需要在一个网页里面同时显示多个画布,并且要做到鼠标聚焦到一个画布之上的时候,我们可以操作这个画布之上的元素,同时多画布存在的这个网页中的其他画布【同源页面多画布】,要取消事件操作带来的影响【事件分发隔离】。
实现效果
核心逻辑
为了在一个页面中,能够展示多个绘制图像的窗口,这些窗口我们使用canvas进行分别绘制。然后在不同的canvas中展示不同的图像,为了更加方便的编写代码操作这些canvas画布。我们引入了PaperJS工具。
PaperJS是一个能够非常方便的操作Canvas的JavaScript工具。在PaperJS中,用户能够非常方便的创建路径,注册多个事件工具。管理Canvas画布的多个图层。并且能够管理同一个页面中的多个Canvas画布。
逻辑步骤整理
- 创建一个HTML页面,在HTML页面中创建多个画布。
- 将这些画布中的paperScope,paperProject,paperView,paperTool分别做状态管理保存下来。
- 当画布得到聚焦的时候,我们后续对于这个画布的操作影响到其他画布。
代码实现
首先创建一个Vue3+Vite项目,语言选择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画布挂载
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()
}
完整源码
<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>
原文链接:https://juejin.cn/post/7261231205353472058 作者:threejs源码翻译官