update 自定义飞线组件

master
管理员 10 months ago
parent 4ac6bae1e2
commit 3d4abe7a19

@ -0,0 +1,135 @@
<template>
<svg
:viewBox="`0 0 ${props.width} ${props.height}`"
xmlns="http://www.w3.org/2000/svg"
>
<g v-for="(line, index) in lines" :key="index">
<!-- 定义阴影滤镜 -->
<defs>
<filter
v-if="line.am && line.dot"
:id="`shadow-${uid}-${index}`"
x="-200%"
y="-200%"
width="500%"
height="500%"
>
<feDropShadow
dx="0"
dy="0"
:stdDeviation="1"
:flood-color="line.flyingColor"
flood-opacity="1"
/>
</filter>
<!-- 定义线性渐变 -->
<linearGradient
v-if="line.am && line.flying"
:id="`gradient-${uid}-${index}`"
x1="0%"
y1="0%"
x2="100%"
y2="0%"
>
<stop offset="0%" style="stop-color: black; stop-opacity: 1" />
<stop offset="100%" style="stop-color: white; stop-opacity: 1" />
</linearGradient>
<!-- 定义mask -->
<mask :id="`mask-${uid}-${index}`" v-if="line.am && line.flying">
<rect
:x="-line.flyingLen"
:y="-line.size"
:width="line.flyingLen"
:height="line.size * 3"
:fill="`url(#gradient-${uid}-${index})`"
>
<animateMotion
:dur="line.time"
repeatCount="indefinite"
rotate="auto"
>
<mpath :href="`#bezierPath-${uid}-${index}`" />
</animateMotion>
</rect>
</mask>
</defs>
<!-- 定义二次贝塞尔曲线路径 -->
<path
:id="`bezierPath-${uid}-${index}`"
:d="line.path"
:stroke-width="line.size"
:stroke="line.lineColor"
stroke-linecap="round"
stroke-linejoin="round"
fill="none"
/>
<path
v-if="line.am && line.flying"
:d="line.path"
:stroke-width="line.size"
:stroke="line.flyingColor"
stroke-linecap="round"
stroke-linejoin="round"
fill="none"
:mask="`url(#mask-${uid}-${index})`"
/>
<!-- 定义光点 -->
<circle
v-if="line.am && line.dot"
cx="0"
cy="0"
:r="(line.size / 5) * 3"
:fill="line.flyingColor"
:filter="`url(#shadow-${uid}-${index})`"
>
<!-- 使用animateMotion让光点沿路径移动 -->
<animateMotion
:dur="line.time"
repeatCount="indefinite"
calcMode="linear"
>
<mpath :href="`#bezierPath-${uid}-${index}`" rotate="reverse" />
</animateMotion>
</circle>
</g>
</svg>
</template>
<script setup>
import { IdGenerator } from "@/util";
import { computed } from "vue";
//
const uid = IdGenerator.next();
const defineLine = {
path: `M 10,10 C 200,10,10,200,290,290`,
size: 1,
lineColor: "var(--w-bg-dark)",
flyingColor: "var(--w-main)",
flyingLen: 20,
flying: true,
dot: true,
am: true,
time: "5s",
};
const props = defineProps({
width: { type: Number, default: 300 },
height: { type: Number, default: 300 },
lines: { type: Array, default: [] },
});
const lines = computed(()=>{
return props.lines.map((line) => {
return {
...defineLine,
...line,
};
});
})
</script>
<style lang="scss" scoped>
</style>

@ -1,5 +1,8 @@
<template>
<div class="index">index</div>
<div class="index">index
</div>
</template>
<script setup>
</script>

@ -0,0 +1,112 @@
<template>
<div s>
<h1>绘制飞线</h1>
<div class="w-flex">
<w-flying-line
:width="width"
:height="height"
:lines="lines"
style="outline: solid #000 1px"
:style="{ width: `${width / 10}rem`, height: `${height / 10}rem` }"
/>
</div>
<div class="form">
<div class="w-flex">
画布宽度width<el-input-number
v-model="width"
:min="100"
:max="1000"
:step="10"
/>
画布高度height<el-input-number
v-model="height"
:min="100"
:max="1000"
:step="10"
/>
</div>
<div>
线条配置lines: path-线条路径, size-线条大小, lineColor-线条颜色,
flyingColor-飞线颜色flyingLen-飞线长度, flying-是否绘制飞线,
dot-是否绘制光点, am-是否启动动画,time-动画周期时间
</div>
<div><el-input type="textarea" v-model="json" rows="10" /></div>
<div>
<el-button @click="lines = JSON.parse(json)">重绘线条</el-button>
</div>
<div>
默认线段配置:
<pre style="background-color: var(--w-bg-light); padding: 1rem; margin: .5rem 0; border: solid .1rem var(-w-bg-dark-5); font-size: .8rem; line-height: 1.8; border-radius: .5rem;">{{ JSON.stringify({
path: `M 10,10 C 200,10,10,200,290,290`,
size: 1,
lineColor: "var(--w-bg-dark)",
flyingColor: "var(--w-main)",
flyingLen: 20,
flying: true,
dot: true,
am: true,
time: "5s",
},null,' ') }}</pre>
</div>
</div>
</div>
</template>
<script setup>
const width = ref(300);
const height = ref(300);
const lines = ref([
{
},
{
path: `M 200,10 Q 290,290,10,290 Z`,
size: 2,
lineColor: "#F002",
flyingColor: "#0F0",
flyingLen: 20,
flying: true,
dot: true,
am: true,
time: "30s",
},
{
path: `M 10,100 C 100,0,200,200,290,100`,
size: 3,
lineColor: "#00f1",
flyingColor: "#00f",
flyingLen: 20,
flying: true,
dot: false,
am: true,
time: "7s",
},
{
path: `M 10,200 C 100,100,200,300,290,200`,
size: 4,
lineColor: "#0a01",
flyingColor: "#0a0",
flyingLen: 20,
flying: false,
dot: true,
am: true,
time: "10s",
},
{
path: `M 5,5 L 295,5 L 295,295 5,295 Z`,
size: 1,
lineColor: "#0003",
am: false,
},
]);
const json = ref(JSON.stringify(lines.value, null, " "));
</script>
<style lang="scss" scoped>
.form {
display: grid;
padding: 1rem 5rem;
grid-template-columns: repeat(1, 1fr);
grid-gap: 1rem;
}
</style>

@ -12,6 +12,8 @@ export const util = {
import * as jsencrypt from './modules/jsencrypt'
export { jsencrypt }
export { IdGenerator} from './modules/IdGenerator'
export { timerBind, timerUnBind } from "./modules/timer";
export { icons } from "./modules/icons";

@ -0,0 +1,26 @@
class IdGenerator {
static #idGenerator = new IdGenerator();
#count = 0;
#countGen() {
this.#count++;
if (this.#count >= 1296) {
this.#count = 0;
}
return this.#count.toString(36).padEnd(2, "0");
}
next() {
return (
Date.now().toString(36) +
this.#countGen() +
Math.random().toString(36).substring(2, 10)
);
}
static next() {
return IdGenerator.#idGenerator.next();
}
}
export { IdGenerator }
Loading…
Cancel
Save