feat: shape masks
diff --git a/public/circle.png b/public/circle.png
new file mode 100644
index 0000000..c553805
--- /dev/null
+++ b/public/circle.png
Binary files differ
diff --git a/public/cloud.png b/public/cloud.png
new file mode 100644
index 0000000..c396d7e
--- /dev/null
+++ b/public/cloud.png
Binary files differ
diff --git a/public/drop.png b/public/drop.png
new file mode 100644
index 0000000..71ff363
--- /dev/null
+++ b/public/drop.png
Binary files differ
diff --git a/public/echarts-0.png b/public/echarts-0.png
new file mode 100644
index 0000000..bd95647
--- /dev/null
+++ b/public/echarts-0.png
Binary files differ
diff --git a/public/echarts-1.png b/public/echarts-1.png
new file mode 100644
index 0000000..0238878
--- /dev/null
+++ b/public/echarts-1.png
Binary files differ
diff --git a/public/flash.png b/public/flash.png
new file mode 100644
index 0000000..4eb9af5
--- /dev/null
+++ b/public/flash.png
Binary files differ
diff --git a/public/flower.png b/public/flower.png
new file mode 100644
index 0000000..07ca318
--- /dev/null
+++ b/public/flower.png
Binary files differ
diff --git a/public/heart.png b/public/heart.png
new file mode 100644
index 0000000..7f66ee2
--- /dev/null
+++ b/public/heart.png
Binary files differ
diff --git a/public/hexagonal.png b/public/hexagonal.png
new file mode 100644
index 0000000..6b89415
--- /dev/null
+++ b/public/hexagonal.png
Binary files differ
diff --git a/public/maps-and-flags.png b/public/maps-and-flags.png
new file mode 100644
index 0000000..1779512
--- /dev/null
+++ b/public/maps-and-flags.png
Binary files differ
diff --git a/public/pentagon.png b/public/pentagon.png
new file mode 100644
index 0000000..3bf2a1e
--- /dev/null
+++ b/public/pentagon.png
Binary files differ
diff --git a/public/question-mark.png b/public/question-mark.png
new file mode 100644
index 0000000..09834c9
--- /dev/null
+++ b/public/question-mark.png
Binary files differ
diff --git a/public/rect.png b/public/rect.png
new file mode 100644
index 0000000..1cd8ded
--- /dev/null
+++ b/public/rect.png
Binary files differ
diff --git a/public/rhombus.png b/public/rhombus.png
new file mode 100644
index 0000000..b7459e5
--- /dev/null
+++ b/public/rhombus.png
Binary files differ
diff --git a/public/rounded-rectangle.png b/public/rounded-rectangle.png
new file mode 100644
index 0000000..e5814fe
--- /dev/null
+++ b/public/rounded-rectangle.png
Binary files differ
diff --git a/public/splash.png b/public/splash.png
new file mode 100644
index 0000000..be7877e
--- /dev/null
+++ b/public/splash.png
Binary files differ
diff --git a/public/star.png b/public/star.png
new file mode 100644
index 0000000..3caf69e
--- /dev/null
+++ b/public/star.png
Binary files differ
diff --git a/public/triangle.png b/public/triangle.png
new file mode 100644
index 0000000..dde4164
--- /dev/null
+++ b/public/triangle.png
Binary files differ
diff --git a/public/wow.png b/public/wow.png
new file mode 100644
index 0000000..dbcdb78
--- /dev/null
+++ b/public/wow.png
Binary files differ
diff --git a/src/components/WChart.vue b/src/components/WChart.vue
index f62e6b3..4d5cce5 100644
--- a/src/components/WChart.vue
+++ b/src/components/WChart.vue
@@ -34,6 +34,7 @@
width: number;
height: number;
shape: string;
+ shapeRatio: boolean;
};
function setLoading(isLoading: boolean) {
@@ -64,37 +65,55 @@
const range = max - min || 1;
return Math.random() * range + min;
}
+ console.log(config?.shapeRatio)
- chart.value!.setOption({
- backgroundColor: config!.bgColor,
- series: [
- {
- type: 'wordCloud',
- data: data || [],
- // gridSize: 0,
- sizeRange: config?.fontSize,
- rotationRange: config?.rotate,
- shape: config?.shape,
- width: config?.width + '%',
- height: config?.height + '%',
- layoutAnimation: true,
- keepAspect: true,
- textStyle: {
- color: (param: any) => {
- const value = param.value;
- const h = getHue();
- const s = getRandom(config?.saturation);
- const l = getRandom(config?.lightness);
- const color = Color(`hsl(${h}, ${s}%, ${l}%)`);
- return color.toString();
+ function render(maskImage?: HTMLImageElement) {
+ chart.value!.setOption({
+ backgroundColor: config!.bgColor,
+ series: [
+ {
+ type: 'wordCloud',
+ data: data || [],
+ gridSize: 4,
+ sizeRange: config?.fontSize,
+ rotationRange: config?.rotate,
+ maskImage,
+ width: config?.width + '%',
+ height: config?.height + '%',
+ layoutAnimation: true,
+ keepAspect: config?.shapeRatio,
+ textStyle: {
+ color: (param: any) => {
+ const value = param.value;
+ const h = getHue();
+ const s = getRandom(config?.saturation);
+ const l = getRandom(config?.lightness);
+ const color = Color(`hsl(${h}, ${s}%, ${l}%)`);
+ return color.toString();
+ }
}
}
+ ],
+ textStyle: {
+ fontFamily: config?.fontFamily
}
- ],
- textStyle: {
- fontFamily: config?.fontFamily
- }
- });
+ });
+ }
+
+ let maskImage: HTMLImageElement;
+ if (config) {
+ maskImage = new Image();
+ maskImage.src = config.shape + '.png';
+ maskImage.onload = () => {
+ render(maskImage);
+ };
+ maskImage.onerror = () => {
+ render();
+ };
+ }
+ else {
+ render();
+ }
}
onMounted(() => {
diff --git a/src/components/WConfig.vue b/src/components/WConfig.vue
index 33d6506..6eb62d4 100644
--- a/src/components/WConfig.vue
+++ b/src/components/WConfig.vue
@@ -6,12 +6,14 @@
<div
class="color-palette"
v-for="palette in colorPalettes"
+ v-bind:key="palette.bgColor"
:style="{ background: palette.bgColor }"
@click="useColorPalette(palette)"
>
<div
class="color"
v-for="color in palette.themeColors"
+ v-bind:key="color"
:style="{ background: color }"
></div>
</div>
@@ -195,22 +197,29 @@
<el-col :span="12">
<h5>遮罩形状</h5>
</el-col>
- <el-col :span="12">
- <el-select
- v-model="selectedMask"
- placeholder="请选择遮罩图形"
- @change="changeMask"
+ <el-col :span="12" class="header-right">
+ <el-checkbox
+ v-model="shapeRatio"
+ @change="change"
>
- <el-option
- v-for="item in masks"
- :key="item.value"
- :label="item.name"
- :value="item.value"
- >
- </el-option>
- </el-select>
+ 保持长宽比
+ </el-checkbox>
</el-col>
</el-row>
+ <div class="img-select"
+ v-for="item in masks"
+ :key="item.value"
+ :value="item.value"
+ v-bind:class="{ selected: item.value === selectedMask }"
+ v-bind:title="item.name"
+ @click="changeMask(item.value)"
+ >
+ <img v-bind:src="item.value + '.png'" />
+ <div class="mark" v-if="item.isFromFreepik">*</div>
+ </div>
+ <div class="hint">
+ 带 * 的形状来自 <a href="https://www.freepik.com" title="Freepik" _target="_blank">Freepik</a>,查看<a href="https://media.flaticon.com/license/license.pdf" _target="_blank">版权</a>
+ </div>
</el-collapse-item>
</el-collapse>
</template>
@@ -249,7 +258,8 @@
const rotate = ref([-90, 90]);
const width = ref(90);
const height = ref(90);
-const selectedMask = ref('circle');
+const selectedMask = ref('heart');
+const shapeRatio = ref(true);
const normalizeFont = (font: string | { name: string; value: string }) => {
if (typeof font === 'string') {
@@ -312,35 +322,94 @@
const masks = [
{
- name: '椭圆',
+ name: 'ECharts 0',
+ value: 'echarts-0'
+ },
+ {
+ name: 'ECharts 1',
+ value: 'echarts-1'
+ },
+ {
+ name: '圆形',
value: 'circle'
},
{
name: '方形',
- value: 'square'
+ value: 'rect'
+ },
+ {
+ name: '云',
+ value: 'cloud',
+ isFromFreepik: true
},
{
name: '菱形',
- value: 'diamond'
+ value: 'rhombus',
+ isFromFreepik: true
},
{
name: '三角形',
- value: 'triangle'
+ value: 'triangle',
+ isFromFreepik: true
},
{
name: '五边形',
- value: 'pentagon'
+ value: 'pentagon',
+ isFromFreepik: true
},
{
name: '五角星',
- value: 'star'
+ value: 'star',
+ isFromFreepik: true
+ },
+ {
+ name: '圆角矩形',
+ value: 'rounded-rectangle',
+ isFromFreepik: true
},
{
name: '爱心',
- value: 'cardioid'
- // }, {
- // name: '自定义图片',
- // value: 'image'
+ value: 'heart',
+ isFromFreepik: true
+ },
+ {
+ name: '六边形',
+ value: 'hexagonal',
+ isFromFreepik: true
+ },
+ {
+ name: '闪电',
+ value: 'flash',
+ isFromFreepik: true
+ },
+ {
+ name: '水滴',
+ value: 'drop',
+ isFromFreepik: true
+ },
+ {
+ name: '六瓣花',
+ value: 'flower',
+ isFromFreepik: true
+ },
+ {
+ name: '喷溅',
+ value: 'splash',
+ isFromFreepik: true
+ },
+ {
+ name: '地标',
+ value: 'maps-and-flags',
+ isFromFreepik: true
+ },
+ {
+ name: '问号',
+ value: 'question-mark',
+ isFromFreepik: true
+ },
+ {
+ name: 'WOW',
+ value: 'wow'
}
];
@@ -373,7 +442,8 @@
change();
}
-function changeMask() {
+function changeMask(value: string) {
+ selectedMask.value = value;
change();
}
@@ -443,12 +513,16 @@
rotate: rotate.value,
width: width.value,
height: height.value,
- shape: selectedMask.value === 'image' ? null : selectedMask.value
+ shape: selectedMask.value,
+ shapeRatio: shapeRatio.value,
};
}
</script>
<style lang="scss">
+$brand-color: #409eff;
+$border-color: #e6e6e6;
+
.title-right {
position: absolute;
top: 10px;
@@ -459,6 +533,10 @@
}
}
+.header-right {
+ text-align: right;
+}
+
.el-color-picker {
margin-right: 5px;
}
@@ -472,9 +550,9 @@
padding: 3px 6px;
box-sizing: border-box;
vertical-align: top;
- border: 1px solid #e6e6e6;
+ border: 1px solid $border-color;
border-radius: 4px;
- color: #409eff;
+ color: $brand-color;
}
.color-palette {
@@ -498,6 +576,45 @@
}
}
+.hint {
+ margin: 5px 0;
+ color: #888;
+
+ a {
+ color: $brand-color;
+ }
+}
+
+.img-select {
+ position: relative;
+ display: inline-block;
+ width: 57px;
+ height: 57px;
+ margin-right: 5px;
+ border: 1px solid $border-color;
+ padding: 5px;
+ border-radius: 4px;
+ opacity: 0.5;
+ cursor: pointer;
+
+ &.selected {
+ border-color: $brand-color;
+ opacity: 1;
+ }
+
+ img {
+ max-width: 100%;
+ max-height: 100%;
+ }
+
+ .mark {
+ position: absolute;
+ top: -2px;
+ right: 4px;
+ color: #000;
+ }
+}
+
.el-checkbox {
--el-checkbox-font-color: #888;
margin-top: 7px;
@@ -509,7 +626,7 @@
.el-dropdown-link {
cursor: pointer;
- color: #409eff;
+ color: $brand-color;
}
.el-icon-arrow-down {