Konva.js 围绕 Rect 元素的波浪笔画
Konva.js waved stroke around Rect element
如果可能的话,将 Rect
元素笔划(边框)更改为波浪样式,如下图所示。
我会让用户通过鼠标移动来绘制Rect
。
我知道如何将边框更改为 Dashed
但波浪样式如何?
这是 Konva.js 和 API 文档的 Rect
示例代码 https://konvajs.org/api/Konva.Rect.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@7.0.3/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Rect Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: "container",
width: width,
height: height,
});
var layer = new Konva.Layer();
var rect1 = new Konva.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
dash: [10, 10],
stroke: "black",
strokeWidth: 4,
});
layer.add(rect1);
stage.add(layer);
</script>
</body>
</html>
所以感谢对这个类似 的出色回答,我给你这个。 @Paul LeBeau 在该答案中的实用方法,已转换为 Konvajs。
这是工作片段 - 也许最好全屏观看。还有一个 codepen here 这样你就可以玩了。
// Set up a stage
let stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
}),
// add a layer to draw on
layer = new Konva.Layer(),
// Add a rect to give us a path as we draw
rubberRect = new Konva.Rect({ X:0, Y:0, width: 0, height: 0, stroke: 'transparent', visible: false}),
// and a path to provide the squiggles
rubberSquiggles = new Konva.Path({
x: rubberRect.position().x,
y: rubberRect.position().y,
data: '',
stroke: 'cyan',
visible: false
});
// Stick it all into the canvas.
layer.add(rubberRect);
layer.add(rubberSquiggles);
stage.add(layer);
// Now we need a primitive state machine and position tracking so that
// we can follow the users mouse when they are scribing a rectangle and
// clone the final rect and path when they mouseup/
// Handy object to store state and rect position drawn
let rectPos = {state: 'waiting', x1:0, y1: 0, x2: 0, y2: 0};
// listen for mousedown
stage.on('mousedown', function(e){
// Set state and note the mouse starting pos.
rectPos = {state: 'drawing', x1: e.evt.layerX , y1: e.evt.layerY, x2: e.evt.layerX, y2: e.evt.layerY};
// ANd position the rubber rect and squiggle line there.
rubberRect.position({x: rectPos.x1, y: rectPos.y1})
rubberSquiggles.position({x: rectPos.x1, y: rectPos.y1});
// better make sure the rubber shapes are on show.
rubberRect.show();
rubberSquiggles.show();
})
// listen for mouse move
stage.on('mousemove', function(e){
// only draw if the mouse state is 'drawing'...
if (rectPos.state === 'drawing'){
// In which case note the mouse pos...
rectPos.x2 = e.evt.layerX;
rectPos.y2 = e.evt.layerY;
// ...and apply to the rubber rect
rubberRect.size({
width: rectPos.x2 - rectPos.x1,
height: rectPos.y2 - rectPos.y1,
});
// and re-calc the squiggles path, then apply it direct to the shape.
rubberSquiggles.data(konvaSquigglePath(rubberRect, 20, 8));
layer.batchDraw();
}
})
// Listen for mouseup
stage.on('mouseup', function(e){
// only draw if the mouse state is 'drawing'...
if (rectPos.state === 'drawing'){
// switch drawing mode off
rectPos.state = 'waiting';
// clone the rubber shapes
let newRect = rubberRect.clone();
let newPath = rubberSquiggles.clone();
layer.add(newRect)
layer.add(newPath)
// hide the rubber shapes until the next draw.
rubberRect.hide();
rubberSquiggles.hide();
}
})
/* this is the func that calculates the path.
Props to the answer by @Paul LeBeau to which I have shamefully adapted here to work with Konvjs.
We pass in the konva rect from which we get the simple path. The clever part is how the
function breaks the path into small lengths and draws beziers alternating along the points.
Play with the squiggleStep and squiggleAmplitude param values to find a pleasent effect.
*/
function konvaSquigglePath(rect, squiggleStep, squiggleAmplitude){
let r = {x1: 0, y1: 0, x2: rect.width(), y2: rect.height() },
// make a simple path around the given rect - is there an easier way ?
p = new Konva.Path({
data: 'M 0 0 H ' + r.x2 + ' V ' + r.y2 + ' H 0 L 0 0'
}),
length = p.getLength(),
pathLen = p.getLength(),
// Adjust step so that there are a whole number of steps along the path
numSteps = Math.round(pathLen / squiggleStep),
pos = p.getPointAtLength(0),
newPath = "M" + [pos.x, pos.y].join(','),
side = -1;
for (let i=1; i<=numSteps; i++)
{
let last = pos;
pos = p.getPointAtLength(i * pathLen / numSteps);
// Find a point halfway between last and pos. Then find the point that is
// perpendicular to that line segment, and is squiggleAmplitude away from
// it on the side of the line designated by 'side' (-1 or +1).
// This point will be the control point of the quadratic curve forming the
// squiggle step.
// The vector from the last point to this one
let vector = {x: (pos.x - last.x),
y: (pos.y - last.y)};
// The length of this vector
let vectorLen = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
// The point halfway between last point and this one
let half = {x: (last.x + vector.x/2),
y: (last.y + vector.y/2)};
// The vector that is perpendicular to 'vector'
let perpVector = {x: -(squiggleAmplitude * vector.y / vectorLen),
y: (squiggleAmplitude * vector.x / vectorLen)};
// No calculate the control point position
let controlPoint = {x: (half.x + perpVector.x * side),
y: (half.y + perpVector.y * side)};
newPath += ("Q" + [controlPoint.x, controlPoint.y, pos.x, pos.y].join(','));
// Switch the side (for next step)
side = -side;
}
return newPath;
}
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
p {
border-bottom: 1px solid #333;
}
<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<p>Draw a rectangle on the white surface.
</p>
<div id="container"></div>
如果可能的话,将 Rect
元素笔划(边框)更改为波浪样式,如下图所示。
我会让用户通过鼠标移动来绘制Rect
。
我知道如何将边框更改为 Dashed
但波浪样式如何?
这是 Konva.js 和 API 文档的 Rect
示例代码 https://konvajs.org/api/Konva.Rect.html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@7.0.3/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Rect Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: "container",
width: width,
height: height,
});
var layer = new Konva.Layer();
var rect1 = new Konva.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
dash: [10, 10],
stroke: "black",
strokeWidth: 4,
});
layer.add(rect1);
stage.add(layer);
</script>
</body>
</html>
所以感谢对这个类似
这是工作片段 - 也许最好全屏观看。还有一个 codepen here 这样你就可以玩了。
// Set up a stage
let stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
}),
// add a layer to draw on
layer = new Konva.Layer(),
// Add a rect to give us a path as we draw
rubberRect = new Konva.Rect({ X:0, Y:0, width: 0, height: 0, stroke: 'transparent', visible: false}),
// and a path to provide the squiggles
rubberSquiggles = new Konva.Path({
x: rubberRect.position().x,
y: rubberRect.position().y,
data: '',
stroke: 'cyan',
visible: false
});
// Stick it all into the canvas.
layer.add(rubberRect);
layer.add(rubberSquiggles);
stage.add(layer);
// Now we need a primitive state machine and position tracking so that
// we can follow the users mouse when they are scribing a rectangle and
// clone the final rect and path when they mouseup/
// Handy object to store state and rect position drawn
let rectPos = {state: 'waiting', x1:0, y1: 0, x2: 0, y2: 0};
// listen for mousedown
stage.on('mousedown', function(e){
// Set state and note the mouse starting pos.
rectPos = {state: 'drawing', x1: e.evt.layerX , y1: e.evt.layerY, x2: e.evt.layerX, y2: e.evt.layerY};
// ANd position the rubber rect and squiggle line there.
rubberRect.position({x: rectPos.x1, y: rectPos.y1})
rubberSquiggles.position({x: rectPos.x1, y: rectPos.y1});
// better make sure the rubber shapes are on show.
rubberRect.show();
rubberSquiggles.show();
})
// listen for mouse move
stage.on('mousemove', function(e){
// only draw if the mouse state is 'drawing'...
if (rectPos.state === 'drawing'){
// In which case note the mouse pos...
rectPos.x2 = e.evt.layerX;
rectPos.y2 = e.evt.layerY;
// ...and apply to the rubber rect
rubberRect.size({
width: rectPos.x2 - rectPos.x1,
height: rectPos.y2 - rectPos.y1,
});
// and re-calc the squiggles path, then apply it direct to the shape.
rubberSquiggles.data(konvaSquigglePath(rubberRect, 20, 8));
layer.batchDraw();
}
})
// Listen for mouseup
stage.on('mouseup', function(e){
// only draw if the mouse state is 'drawing'...
if (rectPos.state === 'drawing'){
// switch drawing mode off
rectPos.state = 'waiting';
// clone the rubber shapes
let newRect = rubberRect.clone();
let newPath = rubberSquiggles.clone();
layer.add(newRect)
layer.add(newPath)
// hide the rubber shapes until the next draw.
rubberRect.hide();
rubberSquiggles.hide();
}
})
/* this is the func that calculates the path.
Props to the answer by @Paul LeBeau to which I have shamefully adapted here to work with Konvjs.
We pass in the konva rect from which we get the simple path. The clever part is how the
function breaks the path into small lengths and draws beziers alternating along the points.
Play with the squiggleStep and squiggleAmplitude param values to find a pleasent effect.
*/
function konvaSquigglePath(rect, squiggleStep, squiggleAmplitude){
let r = {x1: 0, y1: 0, x2: rect.width(), y2: rect.height() },
// make a simple path around the given rect - is there an easier way ?
p = new Konva.Path({
data: 'M 0 0 H ' + r.x2 + ' V ' + r.y2 + ' H 0 L 0 0'
}),
length = p.getLength(),
pathLen = p.getLength(),
// Adjust step so that there are a whole number of steps along the path
numSteps = Math.round(pathLen / squiggleStep),
pos = p.getPointAtLength(0),
newPath = "M" + [pos.x, pos.y].join(','),
side = -1;
for (let i=1; i<=numSteps; i++)
{
let last = pos;
pos = p.getPointAtLength(i * pathLen / numSteps);
// Find a point halfway between last and pos. Then find the point that is
// perpendicular to that line segment, and is squiggleAmplitude away from
// it on the side of the line designated by 'side' (-1 or +1).
// This point will be the control point of the quadratic curve forming the
// squiggle step.
// The vector from the last point to this one
let vector = {x: (pos.x - last.x),
y: (pos.y - last.y)};
// The length of this vector
let vectorLen = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
// The point halfway between last point and this one
let half = {x: (last.x + vector.x/2),
y: (last.y + vector.y/2)};
// The vector that is perpendicular to 'vector'
let perpVector = {x: -(squiggleAmplitude * vector.y / vectorLen),
y: (squiggleAmplitude * vector.x / vectorLen)};
// No calculate the control point position
let controlPoint = {x: (half.x + perpVector.x * side),
y: (half.y + perpVector.y * side)};
newPath += ("Q" + [controlPoint.x, controlPoint.y, pos.x, pos.y].join(','));
// Switch the side (for next step)
side = -side;
}
return newPath;
}
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
p {
border-bottom: 1px solid #333;
}
<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<p>Draw a rectangle on the white surface.
</p>
<div id="container"></div>