效果

缩放与布局自适应
问题:
- 画布缩放超过cantainer时需要可以滚动
- cantainer自适应的话宽高随时在变 画布是针对整个布局通过transform 缩放,则需要动态设置其left 和top保证画布一直对于container位置不变
<div className="layout">
<div className="left">left</div>
<div className="center">
<div className="canvas-container">
<div className="canvas">canvas</div>
</div>
</div>
<div className="right">right</div>
</div>
.layout {
display: flex;
justify-content: space-between;
width: 100%;
height: 100%;
}
.left,
.right {
width: 100px;
height: "100%";
box-sizing: border-box;
border: 1px solid red;
}
.center {
flex: 1;
overflow: auto;
}
- center 宽高自适应 flex: 1 子元素超过center时 可以滚动 scroll:auto
- canvas-container 宽高根据缩放值放大缩小
- 获取父元素center的 width height
- canvas-container 的 width = width * scale / height = height * scale , 当scale > 1 时候 center 就会被撑开
- 监听屏幕 resize center大小 改变时 改变 center的 width height
- canvas 整体对其缩放
- 使用 tranform: scale(number) 改变canvas的大小
- scale会使元素以中心点向两边扩散 需设置left top调整其对于canvas-container 位置不变 left = (scale - 1) / 2 * width top = (scale - 1) / 2 * height
缩放与canvas
效果图

问题
- canvas在缩放的过程中 清晰度也会变化 所以需要动态调整画布的width height 还有线条的宽度
- 画布的网格大小格子边长的动态设置
- 坐标轴设置 (todo 移动时候有辅助线)
代码 不用细讲 感觉贴代码就可以看的很明白了
const CanvasTable: React.FC<IProps> = ({ size, ...props }) => {
const canvasRef = useRef(null)
useLayoutEffect(() => {
if (canvasRef && canvasRef.current && !_.isEmpty(size)) {
const boldWidth = props.boldWidth
const normalWidth = props.normalWidth
const canvas = canvasRef.current as any
const scale = Number(props.scale)
canvas.width = size!.width * scale
canvas.height = size!.height * scale
const context = canvas.getContext("2d")
if (context) {
if (boldWidth) {
const boldW = boldWidth * scale
const boldH = boldWidth * scale
context.lineWidth = 0.5 * scale
context.strokeStyle = "#555"
// 绘制粗的线条
for (let x = 1; x < canvas.width / boldW; x++) {
context.beginPath()
context.moveTo(boldW * x, 0)
context.lineTo(boldW * x, canvas.height)
context.stroke()
}
for (let y = 1; y < canvas.height / boldH; y++) {
context.beginPath()
context.moveTo(0, boldH * y)
context.lineTo(canvas.width, boldH * y)
context.stroke()
}
}
if (normalWidth) {
// 绘制细的线条
const normalW = normalWidth * scale
const normalH = normalWidth * scale
context.lineWidth = 0.3 * scale
context.strokeStyle = "#777"
for (let x = 1; x < canvas.width / normalW; x++) {
context.beginPath()
context.moveTo(normalW * x, 0)
context.lineTo(normalW * x, canvas.height)
context.stroke()
}
for (let y = 1; y < canvas.height / normalH; y++) {
context.beginPath()
context.moveTo(0, normalH * y)
context.lineTo(canvas.width, normalH * y)
context.stroke()
}
}
}
}
return () => {}
}, [props.scale, props.boldWidth, props.normalWidth, size])
if (_.isEmpty(size)) {
return null
}
return (
<canvas
style={{
background: "#fff",
width: `${Number(props.scale) * size!.width}`,
height: `${Number(props.scale) * size!.height}`,
}}
ref={canvasRef}
className={props.className}
>
{props.children}
</canvas>
)
}
坐标轴 效果图

做网格的目的- 自动吸附

代码也很简单 公示一下
const [, drop] = useDrop({
accept: [ItemTypes.EDIT_SHAPE],
drop(item: DragItem, monitor) {
const delta = monitor.getDifferenceFromInitialOffset() as XYCoord
// 计算scale 偏移量
const { id } = item
const originStyle = nodes[id].props.style || {}
let left = Math.round(
parseInt(originStyle.left || "0") + delta.x / Number(scale)
)
let top = Math.round(
parseInt(originStyle.top || "0") + delta.y / Number(scale)
)
const { snapToGridAfterDrop, boldWidth, normalWidth } = props
// 自动吸附
if (snapToGridAfterDrop) {
const dist = normalWidth || boldWidth
// 有小格子先计算小格子
// 没有小格子就算大格子
left = Math.floor(left / dist) * dist
top = Math.floor(top / dist) * dist
}
updateStyle({
style: { left: `${left}px`, top: `${top}px` },
id: item.id,
})
return undefined
},
})
跳着写这系列博文的原因是 我现在写到这里了 正好记录 这样比较简单 勿吐槽 哈哈
欢迎各位小伙伴点赞+关注呀!!!!笔芯❤️