Konva 实现指示框
还是先上效果图
封装
封装成了hooks,可以在vue或者react中使用,也是一份学习资料
代码
从左到右的渐变背景和边框
我采用的是线性渐变,通过createLinearGradient传递两个像素点分别为开始像素点和结束像素点,
然后按照addColorStop来添加各个位置的颜色。
通过moveTo方法来绘制边框
customShape.js
import * as Konva from 'konva'
export class CustomShape extends Konva.Shape {
constructor(config) {
super(config);
this.config = config
const {width, height} = this.config
const rightToLeft = {
x0: width,
y0: height,
x1: 0,
y1: 0,
}
const leftToRight = {
x0: 0,
y0: 0,
x1: width,
y1: height,
}
const length = width >= height ? height * 0.3 : width * 0.3
this.pathConfig = {
RT: [
[0, 0],
[width - length, 0],
[width, length],
[width, height],
[0, height]
],
RB: [
[0, 0],
[width, 0],
[width, height - length],
[width - length, height],
[0, height]
],
LT: [
[length, 0],
[width, 0],
[width, height],
[width, height],
[0, height],
[0, length]
],
LB: [
[0, 0],
[width, 0],
[width, height],
[length, height],
[0, height - length]
],
LR: [
[0, 0],
[width, 0],
[width, height],
[width, height],
[0, height],
],
RL: [
[0, 0],
[width, 0],
[width, height],
[width, height],
[0, height],
]
}
this.linearGradientConfig = {
RT: rightToLeft,
RL: rightToLeft,
RB: rightToLeft,
LT: leftToRight,
LB: leftToRight,
LR: leftToRight
}
this.drawShape()
}
drawShape() {
const {borderWidth, width, height, placement} = this.config
const path = this.pathConfig[placement];
const gradientConfig = this.linearGradientConfig[placement]
if (!path || !gradientConfig) throw 'placement值不正确'
const {x0, y0, x1, y1} = gradientConfig
this.sceneFunc(context => {
context.beginPath();
context.moveTo(path[0][0], path[0][1]);
path.slice(1).forEach((item) => {
context.lineTo(item[0], item[1]);
})
context.lineTo(path[0][0], path[0][1]);
context.closePath();
const gradient = context.createLinearGradient(x0, y0, x1, y1);
gradient.addColorStop(0, 'rgba(87,255,236,0.8)');
// gradient.addColorStop(0.5, 'rgba(87,255,236,0.4)');
gradient.addColorStop(1, 'rgba(87,255,236,0)');
// 应用渐变填充
context.fillStyle = gradient;
context.fill();
// 设置边框颜色
// 创建从上到下的线性渐变用于边框
const borderGradient = context.createLinearGradient(x0, y0, x1, y1);
borderGradient.addColorStop(0, 'rgba(90, 255, 236, 1)');
borderGradient.addColorStop(0.9, 'rgba(90, 255, 236, 0.1)');
borderGradient.addColorStop(1, 'rgba(147, 255, 247, 0)');
// 应用渐变边框
context.strokeStyle = borderGradient;
context.lineWidth = borderWidth; // 使用传入的borderWidth
context.stroke(); // 绘制边框
});
}
}
通过在useBox中的计算使得基准点为圆点的左上角坐标点,
绘制所有图形的时候都以此机基准点
看代码的时候可能有点绕,因为我里面建了一个分组,
分组内部的图形的位置0,0像素点,是相Group的左上角的点位置。
明白这一点代码就比较好懂了
最后返回的boxGroup
可以在外部调用konva
中原有的方法实现,修改图形属性
useBox.js
import * as Konva from 'konva'
import {CustomShape} from "./hooks/customShape.js";
/**
*
* @param layer 图层
* @param baseConfig 基础配置
* @param styleConfig 样式配置
* @return {Konva.Group}
*
* 注意 组里面图形的位置都是相对于组的位置,而我传入的baseX,baseY 的位置都是圆点的坐标,
*/
export function useBox(layer, baseConfig, styleConfig) {
const {baseWidth, baseHeight, baseX, baseY, placement, Cl} = {
Cl: true,
...baseConfig
}
console.log(!placement)
if (!['LT', 'LR', 'LB', 'RT', 'RL', 'RB'].includes(placement)) {
throw new Error('placement must be in \'LT\', \'LR\', \'LB\', \'RT\', \'RL\', \'RB\'')
}
const {fs, px, lineStyle, customShapeStyle, textStyle, circleStyle} = {
fs: 20,
px: 20,
lineStyle: {
strokeWidth: 2
},
customShapeStyle: {},
textStyle: {},
...styleConfig
}
//基础图形起点坐标
let x = baseX
let y = baseY
// 组图形七点坐标
let gx = baseX
let gy = baseY
// 圆形坐标
let cx = baseX
let cy = baseY
// 文字坐标
let tx = baseX
let ty = baseY
let points = []
if (['LT', 'LR', 'LB'].includes(placement)) {
x = baseX + 2 * px
y = y + px
tx = x + fs
ty = y + 2 + ((baseHeight - fs) / 2)
points = [baseX, baseY, x - px, y + baseHeight / 2, x, y + baseHeight / 2]
}
if (['RT', 'RL', 'RB'].includes(placement)) {
gx = baseX - 2 * px
gy = baseY
x = 0 - baseWidth
y = baseY + px
cx = 2 * px
cy = baseY
tx = 0
ty = y + 2 + ((baseHeight - fs) / 2)
points = [cx, cy, cx - px, y + baseHeight / 2, baseWidth + x, y + baseHeight / 2]
}
//创建分组
const boxGroup = new Konva.Group({
width: baseWidth,
height: baseHeight,
x: gx,
y: gy
});
if (Cl) {
//创建圆点
const circle = new Konva.Circle({
x: cx, // 设置圆心 x 坐标
y: cy, // 设置圆心 y 坐标
radius: 7, // 设置圆的半径
fill: 'rgba(87,255,236,1)', // 设置填充颜色
strokeWidth: 0, // 设置描边宽度
...circleStyle
});
boxGroup.add(circle)
}
// 创建基础图形
const customShape = new CustomShape({
x: x,
y: y,
borderWidth: 2,
borderColor: "#FFFFFF",
placement,
...customShapeStyle,
width: baseWidth,
height: baseHeight,
});
boxGroup.add(customShape);
const addText = new Konva.Text({
x: tx,
y: ty,
text: '呼吸 20次/min',
fontSize: fs,
fill: '#FFFFFF',
...textStyle
});
if (['RT', 'RL', 'RB'].includes(placement)) {
addText.x(-addText.textWidth - fs)
}
boxGroup.add(addText);
if (Cl) {
const line = new Konva.Line({
points: points,
stroke: 'rgba(87,255,236,1)',
strokeWidth: 5,
lineCap: 'round',
lineJoin: 'round',
...lineStyle,
});
boxGroup.add(line)
}
layer.add(boxGroup);
return boxGroup
}
使用
useBox
函数支持三个参数,
-
第一参数为建立的图层,
-
第二个参数为整体图形的配置包括长方形的宽
baseWidth
高baseWidth
,
起点像素(圆点的像素点)横坐标baseX
竖坐标baseY
,
以及Cl
是否显示圆圈和长方形和圆圈的连线,
placement
参数属性如下
placement
一共支持六个属性值,分别为LT
透明度从左到右变透明,左上角缺口、LR
透明度从左到右变透明,无缺口、LB
透明度从左到右变透明,左下角缺口、RT
透明度从右到左变透明,右上角缺口、RL
透明度从右到左变透明,无缺口 、RB
透明度从右到左变透明,右下角缺口 -
第三个参数
fs
为字体大小(默认20)px
为圆点距离长方形的位置(默认20)
lineStyle
为一个对象其属性Konva
中line
图形所自有的属性
textStyle
为一个对象其属性Konva
中text
图形所自有的属性
circleStyle
为一个对象其属性Konva
中circle
图形所自有的属性
customShapeStyle
为一个对象其属性Konva
中customShape
图形所自有的属性
init.js
import * as Konva from 'konva'
import {useBox} from "./hooks/useBox.js";
function init() {
const layer = new Konva.Layer();
const stage = new Konva.Stage({
container: 'canvas',
width: 1000,
height: 700,
});
console.log(stage)
stage.add(layer);
const boxLT = useBox(layer, {
baseWidth: 200,
baseHeight: 40,
baseX:260,
baseY: 80,
placement: 'LT',
})
const boxLR = useBox(layer, {
baseWidth: 200,
baseHeight: 40,
baseX:260,
baseY: 105,
placement: 'LR',
})
const boxLB = useBox(layer, {
baseWidth: 200,
baseHeight: 40,
baseX:260,
baseY:130,
placement: 'LB',
})
const boxRT = useBox(layer, {
baseWidth: 200,
baseHeight: 40,
baseX:400,
baseY: 80,
placement: 'RT',
})
const boxRL = useBox(layer, {
baseWidth: 200,
baseHeight: 40,
baseX:400,
baseY: 105,
placement: 'RL',
})
const boxRB = useBox(layer, {
baseWidth: 200,
baseHeight: 40,
baseX:400,
baseY:130,
placement: 'RB',
})
layer.draw()
}