如何一次对多个图形应用透明度?
能否同时对多个图形使用不透明度(opacity)?
你可以使用 opacity 属性来改变任何 Konva 节点的 alpha 通道。由于 canvas 的工作原理,所有的图形都具有各自独立的不透明度值。
这意味着,如果你有一个包含多个图形的组,并且该组设置了 group.opacity(0.5),它的效果与你让每个内部图形都设置 shape.opacity(0.5) 且组的透明度保持 group.opacity(1) 是完全一样的。这会导致这些图形的重叠区域变得透明。
如果我们不希望看到透明图形的重叠区域怎么办?
有一种办法可以修正这种默 认行为。你只需使用 group.cache() 缓存整个组。缓存组会将其转换为位图,并绘制到外部画布上。在下一次绘制调用时,Konva 会用这个生成的画布来绘制整个组,并将不透明度应用于整个图像。
因此,在 Konva 为这样的组生成位图缓存的同时,它会忽略内部形状的透明度,只绘制内部形状的内容。
请记住:如果某个组被缓存,那么它有一些缓存节点的限制。比如你对内部的形状属性进行了更改,你需要重新缓存该组。这个操作较为耗费性能,不建议频繁使用,比如在动画中或每次鼠标移动时都进行。
在下面的示例中,左侧展示默认行为,右侧展示缓存后修正的行为。
试着拖动两组,观察透明度的效果差异。左侧组展示默认的行为,具有可见的重叠区域;右侧组则通过缓存,使整个组作为一个单一的透明单元处理。
- Vanilla
- React
- Vue
import Konva from 'konva';
// 舞台设置
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
// 创建一个包含两个重叠图形的默认组
const group1 = new Konva.Group({
opacity: 0.5,
x: 50,
y: 50,
draggable: true,
});
group1.add(
new Konva.Rect({
width: 100,
height: 100,
fill: 'red',
})
);
group1.add(
new Konva.Circle({
x: 100,
y: 100,
radius: 70,
fill: 'green',
})
);
layer.add(group1);
// 创建第二个组
const group2 = group1.clone({ x: 250 });
layer.add(group2);
// 要改变不透明度行为,只需要缓存整个组
group2.cache();
import { Stage, Layer, Group, Rect, Circle } from 'react-konva';
import { useEffect, useRef, useState } from 'react';
const App = () => {
const group2Ref = useRef(null);
const [groups, setGroups] = useState({
default: { x: 50, y: 50 },
cached: { x: 250, y: 50 },
});
useEffect(() => {
if (group2Ref.current) {
// 缓存第二个组以改变不透明度行为
group2Ref.current.cache();
}
}, []);
const sharedGroupProps = {
opacity: 0.5,
draggable: true,
};
const handleDragEnd = (id, e) => {
setGroups((current) => ({
...current,
[id]: e.target.position(),
}));
};
const renderGroup = (id, ref = null) => (
<Group
{...sharedGroupProps}
x={groups[id].x}
y={groups[id].y}
ref={ref}
onDragEnd={(e) => handleDragEnd(id, e)}
>
<Rect width={100} height={100} fill="red" />
<Circle x={100} y={100} radius={70} fill="green" />
</Group>
);
return (
<Stage width={window.innerWidth} height={400}>
<Layer>
{/* Default group with overlapping shapes */}
{renderGroup('default')}
{/* Cached group with fixed opacity behavior */}
{renderGroup('cached', group2Ref)}
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageConfig">
<v-layer>
<!-- 默认组,包含重叠的图形 -->
<v-group :config="{ ...groupConfig, x: 50 }">
<v-rect :config="rectConfig" />
<v-circle :config="circleConfig" />
</v-group>
<!-- 缓存组,修正了不透明度行为 -->
<v-group ref="group2" :config="{ ...groupConfig, x: 250 }">
<v-rect :config="rectConfig" />
<v-circle :config="circleConfig" />
</v-group>
</v-layer>
</v-stage>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const group2 = ref(null);
const stageConfig = {
width: window.innerWidth,
height: 400,
};
const groupConfig = {
opacity: 0.5,
y: 50,
draggable: true,
};
const rectConfig = {
width: 100,
height: 100,
fill: 'red',
};
const circleConfig = {
x: 100,
y: 100,
radius: 70,
fill: 'green',
};
onMounted(() => {
if (group2.value) {
// 缓存第二个组,修正不透明度行为
group2.value.getNode().cache();
}
});
</script>