使用react-konva制作在线制图应用(6)——图层、缩放画布、删除元素

3,415 阅读2分钟

这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

相关阅读

  1. 使用konva制作在线photoshop(1)——元素拖拽、变形与导出
  2. 使用react-konva制作在线photoshop(2)——字体的文本与样式的修改
  3. 使用react-konva制作在线制图应用(3)——在线字体文件的动态渲染
  4. 使用react-konva制作在线制图应用(4)——撤销/重做(踩坑篇)
  5. 使用react-konva制作在线制图应用(5)——撤销/重做(填坑篇)

图层移动

konva 上图层的顺序其实就是 Layer 的渲染顺序,如下,layer3 就是最上层的,layer1 是最下层的。

<Stage>
    <Layer1 />
    <Layer2 />
    <Layer3 />
</Stage>

我们移动上下图层就是改变 info 中每个图层的 index 值.交换当前图层与目标图层的 index

// i正数往上移动,负数往下移动
Shape.moveLayer = (i: number) => {
    const current = [...steps]
    const currentLayerIndex = current.findIndex((c) => c.id === selectedId)
    let isChanged = false
    if (currentLayerIndex >= 0) {
        const tmp = current[currentLayerIndex]

        if (i > 0) {
            if (currentLayerIndex < current.length - 1) {
                // 界限
                current[currentLayerIndex] = current[currentLayerIndex + 1]
                current[currentLayerIndex + 1] = tmp
                isChanged = true
            }
        } else if (i < 0) {
            if (currentLayerIndex > 0) {
                // 界限
                current[currentLayerIndex] = current[currentLayerIndex - 1]
                current[currentLayerIndex - 1] = tmp
                isChanged = true
            }
        }
    }
    if (isChanged) {
        stepCached.enqueue(current) // 本地数据更改
        setSteps(stepCached.getCurrent()) // 绑定state
    }
}

demo

我们可以看到图层虽然可以正常移动,但是图层移动之后添加文字元素,图层的选中态有问题,看看咋回事。

打印 selectedId 发现,当移动图层后再生成新元素时,新元素的 id 没有递增,查看下生成新元素的代码

// 添加新元素时
const onAdd = (item: IaddItem) => {
    const infos = stepCached.getCurrent()
    const newItem = { ...item, id: 0 }
    const lastInfo = infos[infos.length - 1] // !!!!
    const newId = lastInfo ? lastInfo.id + 1 : 1000
    newItem.id = newId
    const list = [...infos, newItem]
    stepCached.enqueue(list)
    setSteps(stepCached.getCurrent())
    setSelected(newId)
}

发现新元素的 id:newId 是当前 info 数组最后一个元素+1 生成的,这里显然是不太严谨的,应该是当前数组中 id 最大元素+1,这里修改为

const onAdd = (item: IaddItem) => {
    const infos = stepCached.getCurrent()
    const newItem = { ...item, id: 0 }
    const maxId = infos.reduce((prev, info) => Math.max(info.id, prev), 0) // 注意!
    const newId = maxId ? maxId + 1 : 1000
    newItem.id = newId
    const list = [...infos, newItem]
    stepCached.enqueue(list)
    setSteps(stepCached.getCurrent())
    setSelected(newId)
}

最终效果如下

缩放画布

因为我们产品只要求画布中心点缩放,并没有以鼠标为中心进行缩放的功能,就比较简单,直接改变 css transform 属性就 ok 了。

const [stageScale, setStageScale] = useState(1)

// ...
Shape.canvasScale = (ratio = number) => {
    // ratio属于[0.25,2]
    // 获取画布中心的位置
    if (ratio <= 2 && ratio >= 0.25) {
        setStageScale(ratio)
    }
}

return (
    <Stage
        // ....
        style={{ backgroundColor: '#fff', transform: `scale(${stageScale})` }}
    ></Stage>
)

demo

删除元素

删除元素非常好理解,就是删除一个 info 中的所选中的元素。

// 删除选中元素
Shape.deleteItem = () => {
    const info = [...steps]

    const index = info.findIndex((i) => i.id === selectedId)
    if (index >= 0) {
        info.splice(index, 1)
        stepCached.enqueue(info)
        setSteps(stepCached.getCurrent())
    }
}

效果如下: