这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战
前置阅读
- 使用konva制作在线photoshop(1)——元素拖拽、变形与导出
- 使用react-konva制作在线photoshop(2)——字体的文本与样式的修改
- 使用react-konva制作在线制图应用(3)——在线字体文件的动态渲染
- 使用react-konva制作在线制图应用(4)——撤销/重做(踩坑篇)
- 使用react-konva制作在线制图应用(5)——撤销/重做(填坑篇)
- 使用react-konva制作在线制图应用(6)——图层、缩放画布、删除元素
- 使用react-konva制作在线制图应用(7)——用户改变字体宽高和颜色
改变字体加粗斜体样式
我们上文中提过字体的加粗和斜体的展示,通过修改fontStyle这个属性就可以实现;给字体加下划线是改变textDecoration这个属性,代码如下:
const isBold = fontStyle.includes('bold')
const isUnderline = textDecoration.includes('underline')
const isItalic = fontStyle.includes('italic')
const onChange = (key: string, isExsit: boolean) => {
const isDecor = key === 'underline'
let s: string = isDecor ? textDecoration : fontStyle
if (isExsit) {
// 存在就删除
const reg = new RegExp(key)
s = s.replace(reg, '')
} else {
// 不存在就加上
s += ' ' + key
}
onChangeFontStyle({ [isDecor ? 'textDecoration' : 'fontStyle']: s })
}
// ...
<div
className={`button1${isBold ? ' selected' : ''}`}
onClick={onChange.bind(null, 'bold', isBold)}
>
<BoldOutlined />
</div>
<div
className={`button1${isUnderline ? ' selected' : ''}`}
onClick={onChange.bind(null, 'underline', isUnderline)}
>
<UnderlineOutlined />
</div>
<div
className={`button1${isItalic ? ' selected' : ''}`}
onClick={onChange.bind(null, 'italic', isItalic)}
>
<ItalicOutlined />
</div>
)
}
效果如下
修改字体的对齐方式
在 konva 中字体的对齐方式的属性是align,值为:left、center、right。控件的实现方式与上一段类似,只是多选与单选的区别。
const onChange = (align: string) => {
onChangeTextAlign({ align })
}
return (
<div className="fontStyler">
<div
className={`button1${
textAlign === 'left' ? ' selected' : ''
}`}
onClick={onChange.bind(null, 'left')}
>
<AlignLeftOutlined />
</div>
<div
className={`button1${
textAlign === 'center' ? ' selected' : ''
}`}
onClick={onChange.bind(null, 'center')}
>
<AlignCenterOutlined />
</div>
<div
className={`button1${
textAlign === 'right' ? ' selected' : ''
}`}
onClick={onChange.bind(null, 'right')}
>
<AlignRightOutlined />
</div>
</div>
)
}
效果如下:
字体的透明度调节
这里使用antd的 slider 对其进行控制,注意要用 1 减去当前滑动条的值。
!! 这里注意用 debounce 去封装一下 onChange 事件的函数,避免高频更改,使step队列中的无效存放。
const [inputvalue, setInputValue] = useState(0)
const confirmInput = (alpha: any) => {
onChangeOpacity({ opacity: 1 - alpha / 100 })
}
const debounceConfirm = useCallback(debounce(confirmInput, 500), []) // 注意这里
const onChangeSlide = (e: number) => {
setInputValue(e)
debounceConfirm(e)
}
useEffect(() => {
setInputValue(Math.round((1 - opacity) * 100)) // opacity是0-1的小数
}, [opacity])
// ...
return (
<YHSlider
min={0}
max={100}
onChange={onChangeSlide}
value={inputvalue}
step={1}
/>
<YHInput
max={100}
min={0}
step={1}
type="number"
value={inputvalue}
suffix="%"
//@ts-ignore
onChange={(e) => onChangeSlide(e.target.value)}
/>
</div>
)
}
效果如下
旋转角度
实现方式与调节透明度几乎一致,改下参数和最值大小就 ok 了,修改的属性值为rotation
const [inputvalue, setInputValue] = useState(0)
const confirmInput = (deg: any) => {
onChangeRotation({ rotation: Math.round(deg) })
}
const debounceConfirm = useCallback(debounce(confirmInput, 300), [])
const onChangeSlide = (e: number) => {
setInputValue(e)
debounceConfirm(e)
}
useEffect(() => {
setInputValue(Math.round(rotation))
}, [rotation])
return (
<div className="slider">
<YHSlider
min={-180}
max={180}
onChange={onChangeSlide}
value={inputvalue}
step={1}
/>
<YHInput
min={-180}
max={180}
step={1}
type="number"
value={inputvalue}
suffix="°"
onChange={(e) => onChangeSlide(e.target.value)}
/>
)
效果如下
旋转中心
如上图所示,元素的旋转并没有和我们设想的那样,随着元素的中心点旋转,而是围绕元素的左上角旋转(也就是 x、y)的位置,x、y 并没有变化。
查看官方文档 How to set rotation point of a shape?
需要我们自行计算旋转后的 x、y 值,我们改造下官方代码
const handleRotation = (rot: number) => {
const topLeft = {
x: -currentRef.width() / 2,
y: -currentRef.height() / 2,
}
const current = rotatePoint(topLeft, Konva.getAngle(currentRef.rotation()))
const rotated = rotatePoint(topLeft, Konva.getAngle(rot))
const dx = rotated.x - current.x,
dy = rotated.y - current.y
return ({
rotation: Math.round(rot),
x: currentRef.x() + dx,
y: currentRef.y() + dy,
})
}
const confirmInput = (deg: any) => {
const item = handleRotation(deg)
onChangeRotation(item)
}
最终效果如图
大功告成,下章见!