在Konva的 SVG路径中加入粒子效果
我已经创建了一个火焰粒子效果,但无法把它包含在Konva路径内部(因为路径是非规则形状)。我所能做到的最佳效果是在路径周围画一个矩形,但这会导致粒子溢出矩形之外。这些是SVG路径,叠加在一张图片上。
当前的代码是我目前能用的解决方案,直到我能让它工作为止,它只是把圆放在路径(或区域)的中心,并从那里生成粒子。我尝试了关于在fireGroup内使用clipFunc,或globalCompositeOperation('source-in') 的各种建议。最好的情况是没有任何变化,最坏的情况是它会在路径上方或整屏上方创建一个黑色方块。
我希望有人知道一些我可以参考的资源,或给出任何建议来让它工作。我非常感激!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Path Test</title>
<script src="MapData.js"></script>
<script src="konva.js"></script>
<script src="baseImage.js"></script>
<link rel="stylesheet" href="controlpanel.css">
<script src = ""></script>
<style>
body {margin: 0; background:#ffffff; display: flex; justify-content: center; font-family:'Courier New', monospace}
#container {width: 1462; height: 894;background-color:#333;}
</style>
</head>
<body>
<div id="container"></div>
<div id="control-panel">
<h3 id="room-name">Click on a room.</h3>
<button onclick="changeStatus('Fire')">Fire</button>
<button onclick="changeStatus('Clear')">Clear</button>
</div>
<script>
const stage = new Konva.Stage({
container: 'container',width: 1462,height: 894
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.src = baseImage;
imageObj.onload = function(){
const kImg = new Konva.Image({
x: 0, y: 0,
image: imageObj,
width: 1462, height: 894
});
layer.add(kImg);
kImg.moveToBottom();
}
function loadSectors(){
const parser = new DOMParser();
const svgDoc = parser.parseFromString(siteSVG, 'image/svg+xml');
const paths = svgDoc.querySelectorAll('path');
Array.from(paths).forEach(p=> {
const room = new Konva.Path({
data: p.getAttribute('d'),
fill: 'rgba(102,252,241,0.2)',
stroke: 'green',
strokewidth: 1,
name: p.getAttribute('title'),
scaleX: 3.8, scaleY: 3.8,
strokeScaleEnabled: false,
listening: true
});
room.on('mouseenter', ()=> {
room.fill('rgba(102,55,241,0.2)');
layer.draw();
});
room.on('mouseleave', ()=> {
room.fill('rgba(102,252,241,0.2)');
layer.draw();
});
room.on('click', ()=> {
document.getElementById('room-name').innerText=room.name();
window.selectedRoom=room;
})
layer.add(room);
});
layer.draw();
}
loadSectors();
let selectedRoom=null;
function changeStatus(newStatus){
if (!window.selectedRoom) {
alert("Select a room first");
return;
}
if (newStatus==='Fire'){
startFire(window.selectedRoom);
window.selectedRoom.stroke('red');
} else if (newStatus==='Clear')
{
window.selectedRoom.stroke('green');
if (window.selectedRoom.fireAnimation){
window.selectedRoom.fireAnimation.stop();
window.selectedRoom.fireGroup.destroy();
window.selectedRoom.fireAnimation=null;
window.selectedRoom.fireGroup = null;
}
}
layer.draw()
}
function startFire(room){
const box = room.getClientRect();
const layer = room.getLayer();
const centerX = box.x + (box.width / 2);
const centerY = box.y + (box.height / 2);
const fireGroup = new Konva.Group({
name: 'fire-container',
});
layer.add(fireGroup);
const anim = new Konva.Animation((frame) => {
if (Math.random() > 0.1) {
const particle= new Konva.Circle({
x: centerX + (Math.random() * 20 - 10),
y: centerY + (Math.random() * 20 - 10),
radius: Math.random() * 3 + 1,
fill: Math.random() > 0.5 ? 'orange' : 'red',
opacity: 0.8,
});
fireGroup.add(particle);
}
fireGroup.getChildren().forEach(p=> {
p.y(p.y() - 1.5);
p.opacity(p.opacity() - 0.015);
if (p.opacity() <= 0){
p.destroy();
}
});
}, layer);
anim.start();
room.fireAnimation = anim;
room.fireGroup = fireGroup;
}
</script>
</body>
</html>
解决方案
我相信我已经实现了你想要的效果,当你创建你的 fireGroup 时,将 clipFunc 设置为以下函数:
function clipFuncFromPath(shape) {
// All path commands are converted to A, Q, C, L, M or z
// See https://github.com/konvajs/konva/blob/9ebdeba344d9ce294dab9d47ba9fd39691e38738/src/shapes/Path.ts#L442
const cmdToFuncMap = {
A: 'arcTo',
Q: 'quadraticCurveTo',
C: 'bezierCurveTo',
L: 'lineTo',
M: 'moveTo',
z: 'closePath',
};
return (ctx) => {
ctx.save();
ctx.transform(...shape.getTransform().m);
ctx.beginPath();
let closed = false;
for (const { command, points, start } of shape.dataArray) {
if (closed && command !== 'z') {
ctx.moveTo(start.x, start.y);
closed = false;
}
ctx[cmdToFuncMap[command]](...points);
if (command === 'z') {
closed = true;
}
}
ctx.closePath();
ctx.clip();
ctx.restore();
};
}
请参阅 这个fiddle 以获得一个工作示例。
注:我对Konva或画布不是很熟悉,所以可能有更好的实现方式。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。