跳到主要内容

万花筒

万花筒把图像按扇形切片并镜像重复。它是 CSS 滤镜完全没有对应物的效果之一。

用法​

要使用滤镜在Konva.Image我们必须首先使用cache()函数 然后使用滤镜在filter()函数。

要使用Konva创建万花筒,我们可以使用Konva.Filters.Kaleidoscope 滤镜并设置kaleidoscopePower和kaleidoscopeAngle属性。

查看所有可用的滤镜,请查看滤镜文档。

说明:滑动控制以调整万花筒角度。

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Kaleidoscope Image Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
#slider {
position: absolute;
top: 20px;
left: 20px;
}
</style>
</head>
<body>
<div id="container"></div>
<input id="slider" type="range" min="0" max="360" step="5" value="20">
<script>
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
function buildStage(images) {
var stage = new Konva.Stage({
container: 'container',
width: 300,
height: 200
});

var layer = new Konva.Layer();

var lion = new Konva.Image({
image: images.lion,
x: 80,
y: 30,
draggable: true
});

lion.cache();
lion.filters([Konva.Filters.Kaleidoscope]);
lion.kaleidoscopePower(3);
layer.add(lion);
stage.add(layer);
var slider = document.getElementById('slider');
slider.onchange = function() {
lion.kaleidoscopeAngle(slider.value);
layer.batchDraw();
};
}

var sources = {
lion: '/assets/lion.png'
};

loadImages(sources, buildStage);
</script>

</body>
</html>

常见问题​

两个参数分别控制什么?​

kaleidoscopePower 控制切片数量——它是 2 的指数,值为 2 时得到 4 片, 值为 3 时 8 片,以此类推。

kaleidoscopeAngle 控制起始角度,决定从原图的哪个方向开始取样。 改这个值可以在不换图的前提下得到完全不同的图案。

效果和预期差很多?​

万花筒是从图像中心取一个扇形区域再重复的。如果画面主体不在中心, 取到的可能是一块空白或者无关的边角。

调整办法是先把图像位置摆好,让想要的内容落在中心附近, 再应用滤镜。必要时配合 offset 移动节点原点。

为什么这么卡?​

它是所有内置滤镜里计算量最大的之一——每个输出像素都要做坐标变换、 反查源像素、做插值。

静态使用时只在 cache() 那一刻算一次,不成问题。 但如果在动画里逐帧改参数,每帧都是一次全图重算,几乎必然掉帧。

性能提示​

万花筒是「一次性算好、长期复用」的典型场景。

静态图案预先生成。如果参数固定,与其在运行时算,不如把结果 toDataURL() 导出成图片,下次直接当图片用。省掉全部运行时开销。

需要动画时降低分辨率。cache({ pixelRatio: 0.5 }) 让处理的像素数减到四分之一, 万花筒本身是高度对称的图案,分辨率降低不太容易察觉。

不要和其他重滤镜叠加。它已经是最贵的一个,再串一个模糊上去, 开销是相乘而不是相加。确实需要多种效果时,考虑分步预渲染。