feat(数据大屏): 支持装饰组件 (#16788)

This commit is contained in:
王嘉豪
2025-08-21 15:39:10 +08:00
committed by GitHub
parent d971347941
commit e1b7f2d239
25 changed files with 2877 additions and 4 deletions

View File

@@ -74,6 +74,97 @@ export const CANVAS_MATERIAL = [
}
]
},
{
category: 'DeDecoration',
title: t('visualization.decoration'),
span: 8,
details: [
{
value: 'DeDecoration1',
type: 'de_decoration',
title: t('visualization.decoration_name', [1]),
icon: 'DeDecoration1'
},
{
value: 'DeDecoration2',
type: 'de_decoration',
title: t('visualization.decoration_name', [2]),
icon: 'DeDecoration2'
},
{
value: 'DeDecoration3',
type: 'de_decoration',
title: t('visualization.decoration_name', [3]),
icon: 'DeDecoration3'
},
{
value: 'DeDecoration4',
type: 'de_decoration',
title: t('visualization.decoration_name', [4]),
icon: 'DeDecoration4'
},
{
value: 'DeDecoration5',
type: 'de_decoration',
title: t('visualization.decoration_name', [5]),
icon: 'DeDecoration5'
},
{
value: 'DeBoard1',
type: 'de_decoration',
title: t('visualization.decoration_name', [6]),
icon: 'DeBoard1'
},
{
value: 'DeBoard2',
type: 'de_decoration',
title: t('visualization.decoration_name', [7]),
icon: 'DeBoard2'
},
{
value: 'DeBoard3',
type: 'de_decoration',
title: t('visualization.decoration_name', [8]),
icon: 'DeBoard3'
},
{
value: 'DeBoard4',
type: 'de_decoration',
title: t('visualization.decoration_name', [9]),
icon: 'DeBoard4'
},
{
value: 'DeBoard5',
type: 'de_decoration',
title: t('visualization.decoration_name', [10]),
icon: 'DeBoard5'
},
{
value: 'DeBoard6',
type: 'de_decoration',
title: t('visualization.decoration_name', [11]),
icon: 'DeBoard6'
},
{
value: 'DeBoard7',
type: 'de_decoration',
title: t('visualization.decoration_name', [12]),
icon: 'DeBoard7'
},
{
value: 'DeBoard8',
type: 'de_decoration',
title: t('visualization.decoration_name', [13]),
icon: 'DeBoard8'
},
{
value: 'DeBoard10',
type: 'de_decoration',
title: t('visualization.decoration_name', [14]),
icon: 'DeBoard10'
}
]
},
{
category: 'DeGraphical',
title: t('visualization.graphic'),

View File

@@ -4,6 +4,7 @@ import eventBus from '@/utils/eventBus'
import Icon from '@/components/icon-custom/src/Icon.vue'
import { CANVAS_MATERIAL } from '@/custom-component/common/ComponentConfig'
import { ElScrollbar } from 'element-plus-secondary'
import DeDecoration from '@/custom-component/de-decoration/Component.vue'
defineProps({
propValue: {
@@ -48,6 +49,10 @@ const groupActiveChange = category => {
state.curCategory = category
anchorPosition('#' + category)
}
const findUrl = name => {
return new URL(`/src/assets/dynamic-background/${name}`, import.meta.url).href
}
</script>
<template>
@@ -91,6 +96,12 @@ const groupActiveChange = category => {
class-name="item-top-icon"
><component class="svg-icon item-top-icon" :is="chartInfo.icon"></component
></Icon>
<DeDecoration
:curStyle="{ width: 530, height: 373 }"
:element="{ innerType: chartInfo.value }"
:scale="0.15"
v-else-if="['de_decoration'].includes(chartInfo.type)"
></DeDecoration>
<component v-else style="color: #a6a6a6" :is="chartInfo.icon"></component>
</div>
<div v-if="chartInfo.title" class="item-bottom">

View File

@@ -245,7 +245,8 @@ export const commonAttr = {
'videoLinks',
'streamLinks',
'carouselInfo',
'events'
'events',
'decoration_style'
], // 编辑组件时记录当前使用的是哪个折叠面板,再次回来时恢复上次打开的折叠面板,优化用户体验
linkage: {
duration: 0, // 过渡持续时间
@@ -492,6 +493,38 @@ const list = [
backdropFilter: 'blur(0px)'
}
},
{
component: 'DeDecoration',
name: t('visualization.decoration'),
label: t('visualization.decoration'),
propValue: '&nbsp;',
icon: 'dv_decoration',
style: {
width: 400,
height: 300,
color0: '#298e73',
color1: '#2862b7',
color2: '#2862b7',
dur: 6,
reverse: false,
borderActive: false,
backdropFilter: 'blur(0px)'
}
},
{
component: 'DynamicBackground',
name: t('visualization.dynamic_background'),
label: t('visualization.dynamic_background'),
propValue: '&nbsp;',
icon: 'dv_dynamic_background',
style: {
width: 400,
height: 300,
backgroundColor: 'rgba(236,231,231,0.1)',
borderActive: false,
backdropFilter: 'blur(0px)'
}
},
{
component: 'RectShape',
name: t('visualization.rect_shape'),

View File

@@ -0,0 +1,113 @@
<template>
<div class="attr-list de-collapse-style">
<CommonAttr :element="curComponent">
<el-collapse-item
effect="dark"
:title="t('visualization.style')"
name="decoration_style"
v-if="curComponent && !mobileInPc"
>
<div style="display: flex">
<el-tooltip effect="dark" placement="bottom">
<template #content> {{ t('visualization.color_setting', [1]) }} </template>
<el-form-item
effect="dark"
style="margin-left: 12px"
class="form-item no-margin-bottom"
:class="'form-item-dark'"
>
<el-color-picker
:title="t('visualization.color_setting', [1])"
v-model="state.style.color0"
class="color-picker-style"
:prefix-icon="dvStyleColor"
:triggerWidth="80"
is-custom
show-alpha
:predefine="state.predefineColors"
@change="onStyleChange('color0')"
>
</el-color-picker>
</el-form-item>
</el-tooltip>
<el-tooltip effect="dark" placement="bottom">
<template #content> {{ t('visualization.color_setting', [2]) }} </template>
<el-form-item
effect="dark"
style="margin-left: 12px"
class="form-item no-margin-bottom"
:class="'form-item-dark'"
>
<el-color-picker
:title="t('visualization.color_setting', [2])"
v-model="curComponent.style.color1"
class="color-picker-style"
:prefix-icon="dvStyleColor"
:triggerWidth="80"
is-custom
show-alpha
:predefine="state.predefineColors"
@change="onStyleChange('color0')"
>
</el-color-picker>
</el-form-item>
</el-tooltip>
</div>
</el-collapse-item>
</CommonAttr>
</div>
</template>
<script setup lang="ts">
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import CommonAttr from '@/custom-component/common/CommonAttr.vue'
import { storeToRefs } from 'pinia'
import { useI18n } from '@/hooks/web/useI18n'
import { onMounted, reactive, watch } from 'vue'
import { COLOR_PANEL } from '@/views/chart/components/editor/util/chart'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut()
const { curComponent, mobileInPc } = storeToRefs(dvMainStore)
import dvStyleColor from '@/assets/svg/dv-style-color.svg'
const state = reactive({
style: {
color0: null,
color1: null,
color2: null,
dur: 6,
reverse: false
},
predefineColors: COLOR_PANEL
})
const snapshotStore = snapshotStoreWithOut()
const onStyleChange = key => {
curComponent.value.style[key] = state.style[key]
snapshotStore.recordSnapshotCache('decoration')
}
watch(
[() => curComponent.value],
() => {
init()
},
{
deep: true
}
)
onMounted(() => {
init()
})
const init = () => {
setTimeout(() => {
state.style.color0 = curComponent.value.style.color0
state.style.color1 = curComponent.value.style.color1
state.style.color2 = curComponent.value.style.color2
})
}
</script>

View File

@@ -0,0 +1,64 @@
<template>
<div class="dynamic-shape">
<component
:curStyle="curStyleAdaptor"
:scale="calScale"
:is="findDecoration(element.innerType)"
:color="curColor"
></component>
</div>
</template>
<script setup lang="ts">
import { findDecoration } from '@/custom-component/de-decoration/component_details/config'
import { computed } from 'vue'
const calScale = computed(() => {
return props.scale
})
const curStyleAdaptor = computed(() => {
if (props.showPosition.includes('edit')) {
return {
width: parseInt(props.curStyle.width) / props.scale,
height: parseInt(props.curStyle.height) / props.scale
}
} else {
return {
width: parseInt(props.curStyle.width),
height: parseInt(props.curStyle.height)
}
}
})
const curColor = computed(() => {
return [props.element.style?.color0 || null, props.element.style?.color1 || null]
})
const props = defineProps({
curStyle: {
type: Object
},
scale: {
type: Number
},
showPosition: {
required: false,
type: String,
default: 'preview'
},
element: {
type: Object,
default() {
return {
innerType: null
}
}
}
})
</script>
<style lang="less" scoped>
.dynamic-shape {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,162 @@
<template>
<div class="dv-border-box-1" :style="border_style" :ref="refName">
<svg class="border" :width="width" :height="height">
<polygon
:fill="backgroundColor"
:points="`10, 27 10, ${height - 27} 13, ${height - 24} 13, ${height - 21} 24, ${height - 11}
38, ${height - 11} 41, ${height - 8} 73, ${height - 8} 75, ${height - 10} 81, ${height - 10}
85, ${height - 6} ${width - 85}, ${height - 6} ${width - 81}, ${height - 10} ${width - 75}, ${
height - 10
}
${width - 73}, ${height - 8} ${width - 41}, ${height - 8} ${width - 38}, ${height - 11}
${width - 24}, ${height - 11} ${width - 13}, ${height - 21} ${width - 13}, ${height - 24}
${width - 10}, ${height - 27} ${width - 10}, 27 ${width - 13}, 25 ${width - 13}, 21
${width - 24}, 11 ${width - 38}, 11 ${width - 41}, 8 ${width - 73}, 8 ${width - 75}, 10
${width - 81}, 10 ${
width - 85
}, 6 85, 6 81, 10 75, 10 73, 8 41, 8 38, 11 24, 11 13, 21 13, 24`"
/>
</svg>
<svg width="150px" height="150px" :key="item" v-for="item in border" :class="`${item} border`">
<polygon
:fill="mergedColor[0]"
points="6,66 6,18 12,12 18,12 24,6 27,6 30,9 36,9 39,6 84,6 81,9 75,9 73.2,7 40.8,7 37.8,10.2 24,10.2 12,21 12,24 9,27 9,51 7.8,54 7.8,63"
>
<animate
attributeName="fill"
:values="`${mergedColor[0]};${mergedColor[1]};${mergedColor[0]}`"
dur="0.5s"
begin="0s"
repeatCount="indefinite"
/>
</polygon>
<polygon
:fill="mergedColor[1]"
points="27.599999999999998,4.8 38.4,4.8 35.4,7.8 30.599999999999998,7.8"
>
<animate
attributeName="fill"
:values="`${mergedColor[1]};${mergedColor[0]};${mergedColor[1]}`"
dur="0.5s"
begin="0s"
repeatCount="indefinite"
/>
</polygon>
<polygon
:fill="mergedColor[0]"
points="9,54 9,63 7.199999999999999,66 7.199999999999999,75 7.8,78 7.8,110 8.4,110 8.4,66 9.6,66 9.6,54"
>
<animate
attributeName="fill"
:values="`${mergedColor[0]};${mergedColor[1]};transparent`"
dur="1s"
begin="0s"
repeatCount="indefinite"
/>
</polygon>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: object
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => {
return {
width: 320,
height: 240
}
}
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const refName = ref('border-box-1')
const border = ref(['left-top', 'right-top', 'left-bottom', 'right-bottom'])
const defaultColor = ref(['#4fd2dd', '#235fa7'])
const mergedColor = ref<string[]>([])
import { cloneDeep } from 'lodash-es'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0'
}
})
watch(
() => props.color,
() => {
mergeColor()
}
)
onMounted(() => {
mergeColor()
})
</script>
<style lang="less">
.dv-border-box-1 {
position: relative;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
will-change: transform; // 提示浏览器准备变换
.border {
position: absolute;
display: block;
/* 优化SVG渲染 */
shape-rendering: optimizeSpeed;
/* 禁用鼠标事件提高性能 */
pointer-events: none;
}
/* 保持原有角标定位不变 */
.right-top {
right: 0px;
transform: rotateY(180deg) translateZ(0); // 添加硬件加速
}
.left-bottom {
bottom: 0px;
transform: rotateX(180deg) translateZ(0); // 添加硬件加速
}
.right-bottom {
right: 0px;
bottom: 0px;
transform: rotateX(180deg) rotateY(180deg) translateZ(0); // 添加硬件加速
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
/* 创建新的层叠上下文 */
isolation: isolate;
}
}
</style>

View File

@@ -0,0 +1,126 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const border = ['left-top', 'right-top', 'left-bottom', 'right-bottom']
const defaultColor = ref(['#2862b7', '#2862b7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform', // 提示浏览器优化
'box-shadow': `inset 0 0 25px 3px ${mergedColor.value[0]}`,
'--border-color': mergedColor.value[1] // CSS变量传递颜色
}
})
// 使用立即执行的watch
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-10" :style="border_style" ref="dv-border-box-10">
<!-- 主边框SVG -->
<svg class="dv-border-svg-container" :width="width" :height="height">
<polygon
:fill="backgroundColor"
:points="`
4, 0 ${width - 4}, 0 ${width}, 4 ${width}, ${height - 4} ${width - 4}, ${height}
4, ${height} 0, ${height - 4} 0, 4
`"
/>
</svg>
<!-- 四个角标 -->
<template v-for="item in border" :key="item">
<svg width="150px" height="150px" :class="`corner-${item} dv-border-svg-container`">
<polygon fill="var(--border-color)" points="40, 0 5, 0 0, 5 0, 16 3, 19 3, 7 7, 3 35, 3" />
</svg>
</template>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-10 {
position: relative;
width: 100%;
height: 100%;
border-radius: 6px;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.dv-border-svg-container {
position: absolute;
display: block;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none; /* 禁用鼠标事件 */
}
/* 角标位置和变换 */
.corner-left-top {
top: 0;
left: 0;
}
.corner-right-top {
top: 0;
right: 0;
transform: rotateY(180deg) translateZ(0); /* 添加硬件加速 */
}
.corner-left-bottom {
bottom: 0;
left: 0;
transform: rotateX(180deg) translateZ(0); /* 添加硬件加速 */
}
.corner-right-bottom {
bottom: 0;
right: 0;
transform: rotateX(180deg) rotateY(180deg) translateZ(0); /* 添加硬件加速 */
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
}
}
</style>

View File

@@ -0,0 +1,117 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: object
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const defaultColor = ref(['#4fd2dd', '#235fa7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器准备变换
}
})
watch(() => props.color, mergeColor)
onMounted(mergeColor)
</script>
<template>
<!-- 保持原有模板结构不变 -->
<div class="dv-border-box-2" :style="border_style" :ref="ref">
<svg class="dv-border-svg-container" :width="width" :height="height">
<polygon
:fill="backgroundColor"
:points="`
7, 7 ${width - 7}, 7 ${width - 7}, ${height - 7} 7, ${height - 7}
`"
/>
<polyline
:stroke="mergedColor[0]"
:points="`2, 2 ${width - 2} ,2 ${width - 2}, ${height - 2} 2, ${height - 2} 2, 2`"
/>
<polyline
:stroke="mergedColor[1]"
:points="`6, 6 ${width - 6}, 6 ${width - 6}, ${height - 6} 6, ${height - 6} 6, 6`"
/>
<circle :fill="mergedColor[0]" cx="11" cy="11" r="1" />
<circle :fill="mergedColor[0]" :cx="width - 11" cy="11" r="1" />
<circle :fill="mergedColor[0]" :cx="width - 11" :cy="height - 11" r="1" />
<circle :fill="mergedColor[0]" cx="11" :cy="height - 11" r="1" />
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-2 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.dv-border-svg-container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
/* 优化SVG渲染 */
shape-rendering: optimizeSpeed;
pointer-events: none; /* 禁用鼠标事件 */
& > polyline {
fill: none;
stroke-width: 1;
vector-effect: non-scaling-stroke; /* 保持线条宽度不受缩放影响 */
}
& > circle {
will-change: transform; /* 优化小圆点渲染 */
}
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
/* 如果内容不需要交互也可以添加 */
/* pointer-events: none; */
}
}
</style>

View File

@@ -0,0 +1,134 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const defaultColor = ref(['#2862b7', '#2862b7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器准备变换
}
})
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-3" :style="border_style" :ref="ref">
<svg class="dv-border-svg-container" :width="width" :height="height">
<polygon
:fill="backgroundColor"
:points="`
23, 23 ${width - 24}, 23 ${width - 24}, ${height - 24} 23, ${height - 24}
`"
/>
<polyline
class="dv-bb3-line1"
:stroke="mergedColor[0]"
:points="`4, 4 ${width - 22} ,4 ${width - 22}, ${height - 22} 4, ${height - 22} 4, 4`"
/>
<polyline
class="dv-bb3-line2"
:stroke="mergedColor[1]"
:points="`10, 10 ${width - 16}, 10 ${width - 16}, ${height - 16} 10, ${height - 16} 10, 10`"
/>
<polyline
class="dv-bb3-line2"
:stroke="mergedColor[1]"
:points="`16, 16 ${width - 10}, 16 ${width - 10}, ${height - 10} 16, ${height - 10} 16, 16`"
/>
<polyline
class="dv-bb3-line2"
:stroke="mergedColor[1]"
:points="`22, 22 ${width - 4}, 22 ${width - 4}, ${height - 4} 22, ${height - 4} 22, 22`"
/>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-3 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.dv-border-svg-container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none; /* 禁用鼠标事件 */
& > polyline {
fill: none;
vector-effect: non-scaling-stroke; /* 保持线条宽度不受缩放影响 */
}
.dv-bb3-line1 {
stroke-width: 3;
stroke-linecap: round; /* 线条端点圆角 */
}
.dv-bb3-line2 {
stroke-width: 1;
stroke-dasharray: 100; /* 添加虚线效果提升视觉层次 */
stroke-dashoffset: 100;
animation: dash 5s linear infinite;
}
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
}
}
@keyframes dash {
to {
stroke-dashoffset: 0;
}
}
</style>

View File

@@ -0,0 +1,213 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const reverse = computed(() => false)
const defaultColor = ref(['#805151', '#2862b7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器准备变换
}
})
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-4" :style="border_style" :ref="ref">
<svg
:class="`dv-border-svg-container ${reverse && 'dv-reverse'}`"
:width="width"
:height="height"
>
<polygon
:fill="backgroundColor"
:points="`
${width - 15}, 22 170, 22 150, 7 40, 7 28, 21 32, 24
16, 42 16, ${height - 32} 41, ${height - 7} ${width - 15}, ${height - 7}
`"
/>
<polyline
class="dv-bb4-line-1"
:stroke="mergedColor[0]"
:points="`145, ${height - 5} 40, ${height - 5} 10, ${height - 35}
10, 40 40, 5 150, 5 170, 20 ${width - 15}, 20`"
/>
<polyline
:stroke="mergedColor[1]"
class="dv-bb4-line-2"
:points="`245, ${height - 1} 36, ${height - 1} 14, ${height - 23}
14, ${height - 100}`"
/>
<polyline
class="dv-bb4-line-3"
:stroke="mergedColor[0]"
:points="`7, ${height - 40} 7, ${height - 75}`"
/>
<polyline class="dv-bb4-line-4" :stroke="mergedColor[0]" :points="`28, 24 13, 41 13, 64`" />
<polyline class="dv-bb4-line-5" :stroke="mergedColor[0]" :points="`5, 45 5, 140`" />
<polyline class="dv-bb4-line-6" :stroke="mergedColor[1]" :points="`14, 75 14, 180`" />
<polyline
class="dv-bb4-line-7"
:stroke="mergedColor[1]"
:points="`55, 11 147, 11 167, 26 250, 26`"
/>
<polyline class="dv-bb4-line-8" :stroke="mergedColor[1]" :points="`158, 5 173, 16`" />
<polyline
class="dv-bb4-line-9"
:stroke="mergedColor[0]"
:points="`200, 17 ${width - 10}, 17`"
/>
<polyline
class="dv-bb4-line-10"
:stroke="mergedColor[1]"
:points="`385, 17 ${width - 10}, 17`"
/>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-4 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.dv-reverse {
transform: rotate(180deg);
transform-origin: center;
}
.dv-border-svg-container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none; /* 禁用鼠标事件 */
& > polyline {
fill: none;
vector-effect: non-scaling-stroke; /* 保持线条宽度不受缩放影响 */
}
}
.sw1 {
stroke-width: 1;
stroke-linecap: butt;
}
.sw3 {
stroke-width: 3px;
stroke-linecap: round;
}
.dv-bb4-line-1 {
.sw1;
stroke-dasharray: none;
}
.dv-bb4-line-2 {
.sw1;
stroke-dasharray: none;
}
.dv-bb4-line-3 {
.sw3;
stroke-dasharray: none;
}
.dv-bb4-line-4 {
.sw3;
stroke-dasharray: none;
}
.dv-bb4-line-5 {
.sw1;
stroke-dasharray: 2 2; /* 添加虚线效果 */
}
.dv-bb4-line-6 {
.sw1;
stroke-dasharray: 2 2; /* 添加虚线效果 */
}
.dv-bb4-line-7 {
.sw1;
stroke-dasharray: none;
}
.dv-bb4-line-8 {
.sw3;
stroke-dasharray: none;
}
.dv-bb4-line-9 {
.sw3;
stroke-dasharray: 10 5; /* 优化虚线样式 */
animation: dash-animation 3s linear infinite;
}
.dv-bb4-line-10 {
.sw1;
stroke-dasharray: 8 6; /* 优化虚线样式 */
animation: dash-animation 3s linear infinite reverse;
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
}
}
@keyframes dash-animation {
to {
stroke-dashoffset: 15;
}
}
</style>

View File

@@ -0,0 +1,190 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const reverse = computed(() => false)
const defaultColor = ref(['#2862b7', '#2862b7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化变换
}
})
// 使用立即执行的watch
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-5" :style="border_style" ref="dv-border-box-5">
<svg
:class="`dv-border-svg-container ${reverse && 'dv-reverse'}`"
:width="width"
:height="height"
>
<polygon
:fill="backgroundColor"
:points="`
10, 22 ${width - 22}, 22 ${width - 22}, ${height - 86} ${width - 84}, ${height - 24} 10, ${
height - 24
}
`"
/>
<polyline
class="dv-bb5-line-1"
:stroke="mergedColor[0]"
:points="`8, 5 ${width - 5}, 5 ${width - 5}, ${height - 100}
${width - 100}, ${height - 5} 8, ${height - 5} 8, 5`"
/>
<polyline
class="dv-bb5-line-2"
:stroke="mergedColor[1]"
:points="`3, 5 ${width - 20}, 5 ${width - 20}, ${height - 60}
${width - 74}, ${height - 5} 3, ${height - 5} 3, 5`"
/>
<polyline
class="dv-bb5-line-3"
:stroke="mergedColor[1]"
:points="`50, 13 ${width - 35}, 13`"
/>
<polyline
class="dv-bb5-line-4"
:stroke="mergedColor[1]"
:points="`15, 20 ${width - 35}, 20`"
/>
<polyline
class="dv-bb5-line-5"
:stroke="mergedColor[1]"
:points="`15, ${height - 20} ${width - 110}, ${height - 20}`"
/>
<polyline
class="dv-bb5-line-6"
:stroke="mergedColor[1]"
:points="`15, ${height - 13} ${width - 110}, ${height - 13}`"
/>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-5 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.dv-reverse {
transform: rotate(180deg);
transform-origin: center;
}
.dv-border-svg-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none; /* 禁用鼠标事件 */
& > polyline {
fill: none;
vector-effect: non-scaling-stroke; /* 保持线条宽度不受缩放影响 */
}
}
/* 线条样式优化 */
.dv-bb5-line-1 {
stroke-width: 1;
stroke-linecap: round;
stroke-dasharray: none;
}
.dv-bb5-line-2 {
stroke-width: 1;
stroke-linecap: round;
stroke-dasharray: 5 2; /* 添加虚线效果 */
}
.dv-bb5-line-3 {
stroke-width: 5;
stroke-linecap: round;
animation: line-pulse 2s ease-in-out infinite;
}
.dv-bb5-line-4 {
stroke-width: 2;
stroke-linecap: square;
}
.dv-bb5-line-5 {
stroke-width: 2;
stroke-linecap: square;
stroke-dasharray: 10 5; /* 添加虚线效果 */
}
.dv-bb5-line-6 {
stroke-width: 5;
stroke-linecap: round;
animation: line-pulse 2s ease-in-out infinite reverse;
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
}
}
@keyframes line-pulse {
0%,
100% {
opacity: 0.8;
stroke-width: 5;
}
50% {
opacity: 1;
stroke-width: 6;
}
}
</style>

View File

@@ -0,0 +1,194 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const defaultColor = ref(['#2862b7', '#2862b7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化变换
}
})
// 使用立即执行的watch
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-6" :style="border_style" ref="dv-border-box-6">
<svg class="dv-border-svg-container" :width="width" :height="height">
<polygon
:fill="backgroundColor"
:points="`
9, 7 ${width - 9}, 7 ${width - 9}, ${height - 7} 9, ${height - 7}
`"
/>
<!-- 优化圆点渲染 -->
<g class="corner-dots">
<circle :fill="mergedColor[1]" cx="5" cy="5" r="2" />
<circle :fill="mergedColor[1]" :cx="width - 5" cy="5" r="2" />
<circle :fill="mergedColor[1]" :cx="width - 5" :cy="height - 5" r="2" />
<circle :fill="mergedColor[1]" cx="5" :cy="height - 5" r="2" />
</g>
<!-- 水平线 -->
<polyline
class="horizontal-line"
:stroke="mergedColor[0]"
:points="`10, 4 ${width - 10}, 4`"
/>
<polyline
class="horizontal-line"
:stroke="mergedColor[0]"
:points="`10, ${height - 4} ${width - 10}, ${height - 4}`"
/>
<!-- 垂直线 -->
<polyline
class="vertical-line"
:stroke="mergedColor[0]"
:points="`5, 70 5, ${height - 70}`"
/>
<polyline
class="vertical-line"
:stroke="mergedColor[0]"
:points="`${width - 5}, 70 ${width - 5}, ${height - 70}`"
/>
<!-- 装饰短线 -->
<g class="decoration-lines">
<polyline :stroke="mergedColor[0]" :points="`3, 10, 3, 50`" />
<polyline :stroke="mergedColor[0]" :points="`7, 30 7, 80`" />
<polyline :stroke="mergedColor[0]" :points="`${width - 3}, 10 ${width - 3}, 50`" />
<polyline :stroke="mergedColor[0]" :points="`${width - 7}, 30 ${width - 7}, 80`" />
<polyline :stroke="mergedColor[0]" :points="`3, ${height - 10} 3, ${height - 50}`" />
<polyline :stroke="mergedColor[0]" :points="`7, ${height - 30} 7, ${height - 80}`" />
<polyline
:stroke="mergedColor[0]"
:points="`${width - 3}, ${height - 10} ${width - 3}, ${height - 50}`"
/>
<polyline
:stroke="mergedColor[0]"
:points="`${width - 7}, ${height - 30} ${width - 7}, ${height - 80}`"
/>
</g>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-6 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.dv-border-svg-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none; /* 禁用鼠标事件 */
/* 公共线条样式 */
& > polyline {
fill: none;
stroke-width: 1;
vector-effect: non-scaling-stroke; /* 保持线条宽度不受缩放影响 */
}
/* 角点样式 */
.corner-dots > circle {
animation: dot-pulse 2s ease-in-out infinite;
}
/* 水平线样式 */
.horizontal-line {
stroke-dasharray: none;
stroke-linecap: round;
}
/* 垂直线样式 */
.vertical-line {
stroke-dasharray: 5 2;
stroke-linecap: round;
}
/* 装饰短线样式 */
.decoration-lines > polyline {
stroke-dasharray: 3 3;
animation: line-flicker 1.5s ease-in-out infinite alternate;
}
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
}
}
/* 动画定义 */
@keyframes dot-pulse {
0%,
100% {
r: 2;
opacity: 0.8;
}
50% {
r: 3;
opacity: 1;
}
}
@keyframes line-flicker {
0% {
opacity: 0.6;
}
100% {
opacity: 1;
}
}
</style>

View File

@@ -0,0 +1,165 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
})
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const defaultColor = ref(['#2862b7', '#2862b7'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
const [primaryColor, secondaryColor] = mergedColor.value
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform', // 提示浏览器优化
'box-shadow': `inset 0 0 25px ${primaryColor}33`, // 添加透明度
border: `1px solid ${primaryColor}`,
'background-color': props.backgroundColor
}
})
// 使用立即执行的watch并添加防抖
let debounceTimer: number
watch(
() => props.color,
() => {
clearTimeout(debounceTimer)
debounceTimer = setTimeout(mergeColor, 50)
},
{ immediate: true }
)
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-7" :style="border_style" ref="dv-border-box-7">
<!-- 使用分组减少DOM数量 -->
<svg class="dv-border-svg-container" :width="width" :height="height">
<!-- 外层边框 -->
<g class="outer-border">
<polyline :stroke="mergedColor[0]" :points="`0, 25 0, 0 25, 0`" />
<polyline :stroke="mergedColor[0]" :points="`${width - 25}, 0 ${width}, 0 ${width}, 25`" />
<polyline
:stroke="mergedColor[0]"
:points="`${width - 25}, ${height} ${width}, ${height} ${width}, ${height - 25}`"
/>
<polyline
:stroke="mergedColor[0]"
:points="`0, ${height - 25} 0, ${height} 25, ${height}`"
/>
</g>
<!-- 内层边框 -->
<g class="inner-border">
<polyline :stroke="mergedColor[1]" :points="`0, 10 0, 0 10, 0`" />
<polyline :stroke="mergedColor[1]" :points="`${width - 10}, 0 ${width}, 0 ${width}, 10`" />
<polyline
:stroke="mergedColor[1]"
:points="`${width - 10}, ${height} ${width}, ${height} ${width}, ${height - 10}`"
/>
<polyline
:stroke="mergedColor[1]"
:points="`0, ${height - 10} 0, ${height} 10, ${height}`"
/>
</g>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-7 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
overflow: hidden; /* 防止内容溢出 */
.dv-border-svg-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none; /* 禁用鼠标事件 */
& > g > polyline {
fill: none;
stroke-linecap: round;
vector-effect: non-scaling-stroke; /* 保持线条宽度不受缩放影响 */
transition: stroke 0.3s ease; /* 颜色变化动画 */
}
.outer-border > polyline {
stroke-width: 2;
animation: outer-glow 2s ease-in-out infinite alternate;
}
.inner-border > polyline {
stroke-width: 5;
animation: inner-glow 1.5s ease-in-out infinite alternate;
}
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
z-index: 1; /* 确保内容在边框上方 */
}
}
/* 动画定义 */
@keyframes outer-glow {
0% {
stroke-opacity: 0.7;
}
100% {
stroke-opacity: 1;
}
}
@keyframes inner-glow {
0% {
stroke-width: 4;
}
100% {
stroke-width: 6;
}
}
</style>

View File

@@ -0,0 +1,156 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
duration?: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
}),
duration: 3
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const dur = computed(() => props.duration)
const defaultColor = ref(['#2862b7', '#2862b7'])
const mergedColor = ref<string[]>([])
// 生成唯一ID
const generateId = () => Math.random().toString(36).substring(2, 11)
const pathId = `path-${generateId()}`
const gradientId = `gradient-${generateId()}`
const maskId = `mask-${generateId()}`
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const pathD = computed(() => {
return `M2.5,2.5 L${width.value - 2.5},2.5 L${width.value - 2.5},${height.value - 2.5} L2.5,${
height.value - 2.5
} L2.5,2.5`
})
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化变换
}
})
// 使用立即执行的watch
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-8" :style="border_style" ref="dv-border-box-8">
<svg class="dv-border-svg-container" :width="width" :height="height">
<defs>
<!-- 优化路径定义 -->
<path :id="pathId" :d="pathD" fill="none" />
<!-- 优化渐变定义 -->
<radialGradient :id="gradientId" cx="50%" cy="50%" r="50%">
<stop offset="0%" :stop-color="mergedColor[1]" stop-opacity="0.8" />
<stop offset="100%" :stop-color="mergedColor[1]" stop-opacity="0" />
</radialGradient>
<!-- 优化遮罩定义 -->
<mask :id="maskId">
<circle cx="0" cy="0" r="120" :fill="`url(#${gradientId})`">
<animateMotion
:dur="`${dur}s`"
:path="pathD"
rotate="auto"
repeatCount="indefinite"
calcMode="linear"
/>
</circle>
</mask>
</defs>
<!-- 背景多边形 -->
<polygon
:fill="backgroundColor"
:points="`5,5 ${width - 5},5 ${width - 5} ${height - 5} 5,${height - 5}`"
/>
<!-- 静态边框 -->
<use :stroke="mergedColor[0]" stroke-width="1" :href="`#${pathId}`" stroke-linecap="round" />
<!-- 动态发光边框 -->
<use
:stroke="mergedColor[1]"
stroke-width="3"
:href="`#${pathId}`"
:mask="`url(#${maskId})`"
stroke-linecap="round"
>
<animate
attributeName="stroke-dasharray"
:values="`0,${length};${length},0`"
:dur="`${dur}s`"
repeatCount="indefinite"
calcMode="linear"
/>
</use>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-8 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
overflow: hidden;
.dv-border-svg-container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
/* 优化SVG渲染 */
shape-rendering: optimizeSpeed;
pointer-events: none; /* 禁用鼠标事件 */
& > polygon {
vector-effect: non-scaling-stroke;
}
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
z-index: 1;
}
}
</style>

View File

@@ -0,0 +1,230 @@
<script lang="tsx" setup>
import { ref, watch, onMounted, computed } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
backgroundColor?: string
curStyle: { width: number; height: number }
scale: number
duration?: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
backgroundColor: 'transparent',
curStyle: () => ({
width: 320,
height: 240
}),
duration: 10
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
// 生成更简洁的ID
const generateId = () => Math.random().toString(36).substring(2, 8)
const gradientId = `grad-${generateId()}`
const maskId = `mask-${generateId()}`
const defaultColor = ref(['#11eefd', '#0078d2'])
const mergedColor = ref<string[]>([])
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const border_style = computed(() => {
return {
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化
}
})
// 计算关键点坐标
const points = computed(() => ({
// 背景多边形点
bgPoints: `
15,9 ${width.value * 0.1 + 1},9 ${width.value * 0.1 + 4},6 ${width.value * 0.52 + 2},6
${width.value * 0.52 + 6},10 ${width.value * 0.58 - 7},10 ${width.value * 0.58 - 2},6
${width.value * 0.9 + 2},6 ${width.value * 0.9 + 6},10 ${width.value - 10},10 ${
width.value - 10
},${height.value * 0.1 - 6}
${width.value - 6},${height.value * 0.1 - 1} ${width.value - 6},${height.value * 0.8 + 1} ${
width.value - 10
},${height.value * 0.8 + 6}
${width.value - 10},${height.value - 10} ${width.value * 0.92 + 7},${height.value - 10} ${
width.value * 0.92 + 2
},${height.value - 6}
11,${height.value - 6} 11,${height.value * 0.15 - 2} 15,${height.value * 0.15 - 7}
`,
// 遮罩多边形点
maskPoints1: `8,${height.value * 0.4} 8,3 ${width.value * 0.4 + 7},3`,
maskPoints2: `8,${height.value * 0.15} 8,3 ${width.value * 0.1 + 7},3 ${
width.value * 0.1
},8 14,8 14,${height.value * 0.15 - 7}`,
maskPoints3: `${width.value * 0.5},3 ${width.value - 3},3 ${width.value - 3},${
height.value * 0.25
}`,
maskPoints4: `${width.value * 0.52},3 ${width.value * 0.58},3 ${width.value * 0.58 - 7},9 ${
width.value * 0.52 + 7
},9`,
maskPoints5: `${width.value * 0.9},3 ${width.value - 3},3 ${width.value - 3},${
height.value * 0.1
} ${width.value - 9},${height.value * 0.1 - 7} ${width.value - 9},9 ${width.value * 0.9 + 7},9`,
maskPoints6: `8,${height.value * 0.5} 8,${height.value - 3} ${width.value * 0.3 + 7},${
height.value - 3
}`,
maskPoints7: `8,${height.value * 0.55} 8,${height.value * 0.7} 2,${height.value * 0.7 - 7} 2,${
height.value * 0.55 + 7
}`,
maskPoints8: `${width.value * 0.35},${height.value - 3} ${width.value - 3},${height.value - 3} ${
width.value - 3
},${height.value * 0.35}`,
maskPoints9: `${width.value * 0.92},${height.value - 3} ${width.value - 3},${height.value - 3} ${
width.value - 3
},${height.value * 0.8} ${width.value - 9},${height.value * 0.8 + 7} ${width.value - 9},${
height.value - 9
} ${width.value * 0.92 + 7},${height.value - 9}`
}))
// 使用立即执行的watch
watch(() => props.color, mergeColor, { immediate: true })
onMounted(mergeColor)
</script>
<template>
<div class="dv-border-box-9" :style="border_style" ref="dv-border-box-9">
<svg class="dv-border-svg-container" :width="width" :height="height">
<defs>
<!-- 优化渐变定义 -->
<linearGradient :id="gradientId" x1="0%" y1="0%" x2="100%" y2="100%">
<animate
attributeName="x1"
values="0%;100%;0%"
:dur="`${props.duration}s`"
calcMode="linear"
repeatCount="indefinite"
/>
<animate
attributeName="x2"
values="100%;0%;100%"
:dur="`${props.duration}s`"
calcMode="linear"
repeatCount="indefinite"
/>
<stop offset="0%" :stop-color="mergedColor[0]">
<animate
attributeName="stop-color"
:values="`${mergedColor[0]};${mergedColor[1]};${mergedColor[0]}`"
:dur="`${props.duration}s`"
calcMode="linear"
repeatCount="indefinite"
/>
</stop>
<stop offset="100%" :stop-color="mergedColor[1]">
<animate
attributeName="stop-color"
:values="`${mergedColor[1]};${mergedColor[0]};${mergedColor[1]}`"
:dur="`${props.duration}s`"
calcMode="linear"
repeatCount="indefinite"
/>
</stop>
</linearGradient>
<!-- 优化遮罩定义 -->
<mask :id="maskId">
<polyline
stroke="#fff"
stroke-width="3"
fill="transparent"
:points="points.maskPoints1"
/>
<polyline fill="#fff" :points="points.maskPoints2" />
<polyline
stroke="#fff"
stroke-width="3"
fill="transparent"
:points="points.maskPoints3"
/>
<polyline fill="#fff" :points="points.maskPoints4" />
<polyline fill="#fff" :points="points.maskPoints5" />
<polyline
stroke="#fff"
stroke-width="3"
fill="transparent"
:points="points.maskPoints6"
/>
<polyline fill="#fff" :points="points.maskPoints7" />
<polyline
stroke="#fff"
stroke-width="3"
fill="transparent"
:points="points.maskPoints8"
/>
<polyline fill="#fff" :points="points.maskPoints9" />
</mask>
</defs>
<!-- 背景多边形 -->
<polygon :fill="backgroundColor" :points="points.bgPoints" />
<!-- 渐变矩形 -->
<rect
x="0"
y="0"
:width="width"
:height="height"
:fill="`url(#${gradientId})`"
:mask="`url(#${maskId})`"
/>
</svg>
<div class="border-box-content">
<slot></slot>
</div>
</div>
</template>
<style lang="less">
.dv-border-box-9 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
overflow: hidden;
.dv-border-svg-container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
/* 优化SVG渲染 */
shape-rendering: optimizeSpeed;
pointer-events: none; /* 禁用鼠标事件 */
& > polygon,
& > rect {
vector-effect: non-scaling-stroke;
}
}
.border-box-content {
position: relative;
width: 100%;
height: 100%;
isolation: isolate; /* 创建新的层叠上下文 */
z-index: 1; /* 确保内容在边框上方 */
}
}
</style>

View File

@@ -0,0 +1,202 @@
<template>
<div class="dv-decoration-1" :style="border_style" :ref="refName">
<svg
:width="`${svgWH[0]}px`"
:height="`${svgWH[1]}px`"
:style="`transform:scale(${svgScale[0]},${svgScale[1]});`"
>
<template v-for="(point, i) in points" :key="i">
<rect
v-if="Math.random() > 0.6"
:fill="mergedColor[0]"
:x="point[0] - halfPointSideLength"
:y="point[1] - halfPointSideLength"
:width="pointSideLength"
:height="pointSideLength"
>
<animate
v-if="Math.random() > 0.6"
attributeName="fill"
:values="`${mergedColor[0]};transparent`"
dur="1s"
:begin="Math.random() * 2"
repeatCount="indefinite"
/>
</rect>
</template>
<rect
v-if="rects[0]"
:fill="mergedColor[1]"
:x="rects[0][0] - pointSideLength"
:y="rects[0][1] - pointSideLength"
:width="pointSideLength * 2"
:height="pointSideLength * 2"
>
<animate
attributeName="width"
:values="`0;${pointSideLength * 2}`"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="height"
:values="`0;${pointSideLength * 2}`"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="x"
:values="`${rects[0][0]};${rects[0][0] - pointSideLength}`"
dur="2s"
repeatCount="indefinite"
/>
<animate
attributeName="y"
:values="`${rects[0][1]};${rects[0][1] - pointSideLength}`"
dur="2s"
repeatCount="indefinite"
/>
</rect>
<rect
v-if="rects[1]"
:fill="mergedColor[1]"
:x="rects[1][0] - 40"
:y="rects[1][1] - pointSideLength"
:width="40"
:height="pointSideLength * 2"
>
<animate attributeName="width" values="0;40;0" dur="2s" repeatCount="indefinite" />
<animate
attributeName="x"
:values="`${rects[1][0]};${rects[1][0] - 40};${rects[1][0]}`"
dur="2s"
repeatCount="indefinite"
/>
</rect>
</svg>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { cloneDeep } from 'lodash-es'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
interface Props {
color?: string[]
curStyle: object
scale: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
curStyle: () => {
return {
width: 320,
height: 240
}
}
})
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
const border_style = computed(() => ({
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化
}))
const pointSideLength = 2.5
const refName = ref('decoration-1')
const svgWH = ref([200, 50])
const svgScale = ref([1, 1])
const rowNum = 4
const rowPoints = 20
const halfPointSideLength = computed(() => pointSideLength / 2)
const points = ref<number[][]>([])
const rects = ref<number[][]>([])
const defaultColor = ref(['#fff', '#0de7c2'])
const mergedColor = ref<string[]>([])
const calcPointsPosition = () => {
const [w, h] = svgWH.value
const horizontalGap = w / (rowPoints + 1)
const verticalGap = h / (rowNum + 1)
let pointsArray = new Array(rowNum)
.fill(0)
.map((_, i) =>
new Array(rowPoints).fill(0).map((_, j) => [horizontalGap * (j + 1), verticalGap * (i + 1)])
)
points.value = pointsArray.reduce((all, item) => [...all, ...item], [])
}
const calcRectsPosition = () => {
const rect1 = points.value[rowPoints * 2 - 1]
const rect2 = points.value[rowPoints * 2 - 3]
rects.value = [rect1, rect2]
}
const calcScale = () => {
const [w, h] = svgWH.value
svgScale.value = [width.value / w, height.value / h]
}
const calcSVGData = () => {
calcPointsPosition()
calcRectsPosition()
calcScale()
}
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
const onResize = () => {
calcSVGData()
}
watch(
() => props.color,
() => {
mergeColor()
}
)
onMounted(() => {
mergeColor()
calcSVGData()
})
// Handle autoResize mixin
// Note: In Vue 3, consider converting autoResize to a composable
watch([width, height], () => {
onResize()
})
</script>
<style lang="less">
.dv-decoration-1 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
svg {
position: absolute;
transform-origin: left top;
/* 优化SVG渲染 */
shape-rendering: optimizeSpeed;
pointer-events: none;
}
}
</style>

View File

@@ -0,0 +1,136 @@
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
curStyle: { width: number; height: number }
scale: number
reverse?: boolean
dur?: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
curStyle: () => ({
width: 320,
height: 240
}),
reverse: false,
dur: 6
})
// 尺寸计算
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
// 样式计算
const border_style = computed(() => ({
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化
}))
// 颜色配置
const defaultColor = ref(['#3faacb', '#fff'])
const mergedColor = ref<string[]>([])
// SVG元素位置和尺寸
const svgElements = computed(() => ({
x: props.reverse ? width.value / 2 : 0,
y: props.reverse ? 0 : height.value / 2,
w: props.reverse ? 1 : width.value,
h: props.reverse ? height.value : 1
}))
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
// 监听器
watch(() => props.color, mergeColor, { immediate: true })
watch(() => props.reverse, mergeColor)
// 生命周期
onMounted(mergeColor)
</script>
<template>
<div class="dv-decoration-2" :style="border_style">
<svg :width="`${width}px`" :height="`${height}px`" class="decoration-svg">
<!-- 主矩形动画 -->
<rect
:x="svgElements.x"
:y="svgElements.y"
:width="svgElements.w"
:height="svgElements.h"
:fill="mergedColor[0]"
class="main-rect"
>
<animate
:attributeName="reverse ? 'height' : 'width'"
from="0"
:to="reverse ? height : width"
:dur="`${dur}s`"
calcMode="spline"
keyTimes="0;1"
keySplines=".42,0,.58,1"
repeatCount="indefinite"
/>
</rect>
<!-- 小点动画 -->
<rect
:x="svgElements.x"
:y="svgElements.y"
width="1"
height="1"
:fill="mergedColor[1]"
class="dot-rect"
>
<animate
:attributeName="reverse ? 'y' : 'x'"
from="0"
:to="reverse ? height : width"
:dur="`${dur}s`"
calcMode="spline"
keyTimes="0;1"
keySplines="0.42,0,0.58,1"
repeatCount="indefinite"
/>
</rect>
</svg>
</div>
</template>
<style lang="less">
.dv-decoration-2 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.decoration-svg {
position: absolute;
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none;
.main-rect {
opacity: 0.8;
transition: fill 0.3s ease;
}
.dot-rect {
rx: 50%; /* 圆形点 */
transition: fill 0.3s ease;
}
}
}
</style>

View File

@@ -0,0 +1,146 @@
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
curStyle: { width: number; height: number }
scale: number
animationRatio?: number // 新增:控制动画元素比例
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
curStyle: () => ({
width: 320,
height: 240
}),
animationRatio: 0.6 // 默认60%的元素有动画
})
// 尺寸计算
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
// 样式计算
const border_style = computed(() => ({
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化
}))
// 点阵配置
const pointSideLength = 7
const svgWH = ref([300, 35])
const rowNum = 2
const rowPoints = 25
// 颜色配置
const defaultColor = ref(['#7acaec', 'transparent'])
const mergedColor = ref<string[]>([])
// 计算属性
const halfPointSideLength = computed(() => pointSideLength / 2)
const svgScale = computed(() => [width.value / svgWH.value[0], height.value / svgWH.value[1]])
// 点阵位置
const points = ref<number[][]>([])
// 预生成动画配置(优化随机性能)
const animationConfigs = computed(() => {
return points.value.map((_, i) => ({
shouldAnimate: Math.random() <= props.animationRatio,
duration: 1 + Math.random(), // 1-2秒
delay: Math.random() * 2 // 0-2秒
}))
})
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
// 计算点阵位置
const calcPointsPosition = () => {
const [w, h] = svgWH.value
const horizontalGap = w / (rowPoints + 1)
const verticalGap = h / (rowNum + 1)
points.value = Array.from({ length: rowNum }, (_, i) =>
Array.from({ length: rowPoints }, (_, j) => [horizontalGap * (j + 1), verticalGap * (i + 1)])
).flat()
}
// 响应式更新
const updateLayout = () => {
calcPointsPosition()
}
// 生命周期和监听
onMounted(() => {
mergeColor()
updateLayout()
})
watch(() => props.color, mergeColor, { immediate: true })
watch([width, height], updateLayout)
</script>
<template>
<div class="dv-decoration-3" :style="border_style">
<svg
:width="`${svgWH[0]}px`"
:height="`${svgWH[1]}px`"
:style="`transform:scale(${svgScale[0]},${svgScale[1]});`"
class="decoration-svg"
>
<rect
v-for="(point, i) in points"
:key="i"
:fill="mergedColor[0]"
:x="point[0] - halfPointSideLength"
:y="point[1] - halfPointSideLength"
:width="pointSideLength"
:height="pointSideLength"
rx="1"
opacity="0.8"
>
<animate
v-if="animationConfigs[i].shouldAnimate"
attributeName="fill"
:values="mergedColor.join(';')"
:dur="`${animationConfigs[i].duration}s`"
:begin="`${animationConfigs[i].delay}s`"
repeatCount="indefinite"
calcMode="linear"
/>
</rect>
</svg>
</div>
</template>
<style lang="less">
.dv-decoration-3 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.decoration-svg {
position: absolute;
transform-origin: left top;
/* 优化SVG渲染 */
shape-rendering: optimizeSpeed;
pointer-events: none;
rect {
transition: fill 0.3s ease; /* 颜色变化过渡 */
}
}
}
</style>

View File

@@ -0,0 +1,155 @@
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
curStyle: { width: number; height: number }
scale: number
reverse?: boolean
dur?: number
strokeWidth?: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
curStyle: () => ({
width: 320,
height: 240
}),
reverse: false,
dur: 6,
strokeWidth: 3
})
// 尺寸计算
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
// 样式计算
const border_style = computed(() => ({
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化
}))
// 颜色配置
const defaultColor = ref(['rgba(255, 255, 255, 0.3)', 'rgba(255, 255, 255, 0.3)'])
const mergedColor = ref<string[]>([])
// 计算属性
const containerStyle = computed(() => ({
width: props.reverse ? `${width.value}px` : `${props.strokeWidth}px`,
height: props.reverse ? `${props.strokeWidth}px` : `${height.value}px`,
'--animation-duration': `${props.dur}s`,
'--stroke-width': `${props.strokeWidth}px`,
'--half-stroke': `${props.strokeWidth / 2}px`
}))
const svgPoints = computed(() =>
props.reverse
? `0, ${props.strokeWidth / 2} ${width.value}, ${props.strokeWidth / 2}`
: `${props.strokeWidth / 2}, 0 ${props.strokeWidth / 2}, ${height.value}`
)
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
// 生命周期和监听
onMounted(mergeColor)
watch(() => props.color, mergeColor, { immediate: true })
</script>
<template>
<div class="dv-decoration-4" :style="border_style">
<div :class="['container', reverse ? 'reverse' : 'normal']" :style="containerStyle">
<svg
:width="reverse ? width : strokeWidth"
:height="reverse ? strokeWidth : height"
class="decoration-svg"
>
<!-- 细线 -->
<polyline :stroke="mergedColor[0]" :points="svgPoints" stroke-linecap="round" />
<!-- 粗虚线 -->
<polyline
class="bold-line"
:stroke="mergedColor[1]"
:stroke-width="strokeWidth"
stroke-dasharray="20, 80"
stroke-dashoffset="-30"
:points="svgPoints"
stroke-linecap="round"
/>
</svg>
</div>
</div>
</template>
<style lang="less">
.dv-decoration-4 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.container {
position: absolute;
overflow: hidden;
will-change: transform;
&.normal {
left: 50%;
margin-left: calc(-1 * var(--half-stroke));
animation: ani-height var(--animation-duration) ease-in-out infinite;
}
&.reverse {
top: 50%;
margin-top: calc(-1 * var(--half-stroke));
animation: ani-width var(--animation-duration) ease-in-out infinite;
}
}
.decoration-svg {
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none;
polyline {
transition: stroke 0.3s ease;
}
}
@keyframes ani-height {
0% {
height: 0;
transform: translateY(100%);
}
70%,
100% {
height: 100%;
transform: translateY(0);
}
}
@keyframes ani-width {
0% {
width: 0;
transform: translateX(100%);
}
70%,
100% {
width: 100%;
transform: translateX(0);
}
}
}
</style>

View File

@@ -0,0 +1,161 @@
<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { customMergeColor } from '@/custom-component/de-decoration/component_details/config'
import { cloneDeep } from 'lodash-es'
interface Props {
color?: string[]
curStyle: { width: number; height: number }
scale: number
dur?: number
line1Width?: number
line2Width?: number
}
const props = withDefaults(defineProps<Props>(), {
color: () => [],
curStyle: () => ({
width: 320,
height: 240
}),
dur: 3,
line1Width: 3,
line2Width: 2
})
// 尺寸计算
const width = computed(() => props.curStyle.width)
const height = computed(() => props.curStyle.height)
// 样式计算
const border_style = computed(() => ({
width: `${width.value}px`,
height: `${height.value}px`,
transform: `scale(${props.scale})`,
'transform-origin': '0 0',
'will-change': 'transform' // 提示浏览器优化
}))
// 颜色配置
const defaultColor = ref(['#3f96a5', '#3f96a5'])
const mergedColor = ref<string[]>([])
// 路径数据
const svgPaths = computed(() => {
const line1Points = [
[0, height.value * 0.2],
[width.value * 0.18, height.value * 0.2],
[width.value * 0.2, height.value * 0.4],
[width.value * 0.25, height.value * 0.4],
[width.value * 0.27, height.value * 0.6],
[width.value * 0.72, height.value * 0.6],
[width.value * 0.75, height.value * 0.4],
[width.value * 0.8, height.value * 0.4],
[width.value * 0.82, height.value * 0.2],
[width.value, height.value * 0.2]
]
const line2Points = [
[width.value * 0.3, height.value * 0.8],
[width.value * 0.7, height.value * 0.8]
]
// 计算路径长度(简化版,实际项目中可以使用更精确的计算)
const calculateLength = (points: number[][]) => {
let length = 0
for (let i = 1; i < points.length; i++) {
const [x1, y1] = points[i - 1]
const [x2, y2] = points[i]
length += Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
}
return length
}
return {
line1: {
points: line1Points.map(p => p.join(',')).join(' '),
length: calculateLength(line1Points),
halfLength: calculateLength(line1Points) / 2
},
line2: {
points: line2Points.map(p => p.join(',')).join(' '),
length: calculateLength(line2Points),
halfLength: calculateLength(line2Points) / 2
}
}
})
const mergeColor = () => {
mergedColor.value = customMergeColor(cloneDeep(defaultColor.value), props.color)
}
// 生命周期和监听
onMounted(mergeColor)
watch(() => props.color, mergeColor, { immediate: true })
</script>
<template>
<div class="dv-decoration-5" :style="border_style">
<svg :width="width" :height="height" class="decoration-svg">
<!-- 主线条动画 -->
<polyline
fill="transparent"
:stroke="mergedColor[0]"
:stroke-width="line1Width"
stroke-linecap="round"
:points="svgPaths.line1.points"
>
<animate
attributeName="stroke-dasharray"
:values="`0,${svgPaths.line1.halfLength},0,${svgPaths.line1.halfLength};0,0,${svgPaths.line1.length},0`"
:dur="`${dur}s`"
calcMode="spline"
keyTimes="0;1"
keySplines="0.4,1,0.49,0.98"
repeatCount="indefinite"
/>
</polyline>
<!-- 副线条动画 -->
<polyline
fill="transparent"
:stroke="mergedColor[1]"
:stroke-width="line2Width"
stroke-linecap="round"
:points="svgPaths.line2.points"
>
<animate
attributeName="stroke-dasharray"
:values="`0,${svgPaths.line2.halfLength},0,${svgPaths.line2.halfLength};0,0,${svgPaths.line2.length},0`"
:dur="`${dur}s`"
calcMode="spline"
keyTimes="0;1"
keySplines="0.4,1,0.49,0.98"
repeatCount="indefinite"
/>
</polyline>
</svg>
</div>
</template>
<style lang="less">
.dv-decoration-5 {
position: relative;
width: 100%;
height: 100%;
/* 启用硬件加速 */
transform: translateZ(0);
backface-visibility: hidden;
contain: content; /* 限制重绘范围 */
.decoration-svg {
/* 优化SVG渲染 */
shape-rendering: crispEdges;
pointer-events: none;
polyline {
transition: stroke 0.3s ease;
}
}
}
</style>

View File

@@ -0,0 +1,61 @@
import DeBoard1 from '@/custom-component/de-decoration/component_details/DeBoard1.vue'
import DeBoard2 from '@/custom-component/de-decoration/component_details/DeBoard2.vue'
import DeBoard3 from '@/custom-component/de-decoration/component_details/DeBoard3.vue'
import DeBoard4 from '@/custom-component/de-decoration/component_details/DeBoard4.vue'
import DeBoard5 from '@/custom-component/de-decoration/component_details/DeBoard5.vue'
import DeBoard6 from '@/custom-component/de-decoration/component_details/DeBoard6.vue'
import DeBoard7 from '@/custom-component/de-decoration/component_details/DeBoard7.vue'
import DeBoard8 from '@/custom-component/de-decoration/component_details/DeBoard8.vue'
import DeBoard9 from '@/custom-component/de-decoration/component_details/DeBoard9.vue'
import DeBoard10 from '@/custom-component/de-decoration/component_details/DeBoard10.vue'
import DeDecoration1 from '@/custom-component/de-decoration/component_details/DeDecoration1.vue'
import DeDecoration2 from '@/custom-component/de-decoration/component_details/DeDecoration2.vue'
import DeDecoration3 from '@/custom-component/de-decoration/component_details/DeDecoration3.vue'
import DeDecoration4 from '@/custom-component/de-decoration/component_details/DeDecoration4.vue'
import DeDecoration5 from '@/custom-component/de-decoration/component_details/DeDecoration5.vue'
const boardInfoMap = {
DeBoard1: DeBoard1,
DeBoard2: DeBoard2,
DeBoard3: DeBoard3,
DeBoard4: DeBoard4,
DeBoard5: DeBoard5,
DeBoard6: DeBoard6,
DeBoard7: DeBoard7,
DeBoard8: DeBoard8,
DeBoard9: DeBoard9,
DeBoard10: DeBoard10,
DeDecoration1: DeDecoration1,
DeDecoration2: DeDecoration2,
DeDecoration3: DeDecoration3,
DeDecoration4: DeDecoration4,
DeDecoration5: DeDecoration5
}
export const findDecoration = name => {
return boardInfoMap[name]
}
export const calcTwoPointDistance = (pointA, pointB) => {
const minusX = Math.abs(pointA[0] - pointB[0])
const minusY = Math.abs(pointA[1] - pointB[1])
return Math.sqrt(Math.pow(minusX, 2) + Math.pow(minusY, 2))
}
/**
* @description 获取多个点,每个点之间的距离
* @param {Point[]} points
* @return {number[]}
*/
export function getPointDistances(points) {
return new Array(points.length - 1)
.fill(0)
.map((_, i) => calcTwoPointDistance(points[i], points[i + 1]))
}
export function customMergeColor(defaultColor: string[], newColor: []) {
return defaultColor.map((defaultVal, index) => {
return newColor && newColor[index] !== null ? newColor[index] : defaultVal
})
}

View File

@@ -504,9 +504,15 @@ export function adaptCurThemeCommonStyle(component) {
// 背景融合-Begin 如果是大屏['CanvasBoard', 'CanvasIcon', 'Picture']组件不需要设置背景
if (
dvMainStore.dvInfo.type === 'dataV' &&
['CanvasBoard', 'CanvasIcon', 'Picture', 'Group', 'SvgTriangle', 'SvgStar'].includes(
component.component
)
[
'CanvasBoard',
'CanvasIcon',
'Picture',
'Group',
'SvgTriangle',
'SvgStar',
'DeDecoration'
].includes(component.component)
) {
component.commonBackground['backgroundColorSelect'] = false
component.commonBackground['innerPadding'] = 0

View File

@@ -96,6 +96,9 @@ export function findNewComponent(componentName, innerType, staticMap?) {
if (newComponent.isPlugin) {
newComponent.staticMap = staticMap
}
} else if (['DeDecoration', 'DynamicBackground'].includes(componentName)) {
newComponent.style.borderWidth = 0
newComponent.style.innerPadding = 0
}
return newComponent
}

View File

@@ -19,6 +19,7 @@ import ScrollText from '@/custom-component/scroll-text/Component.vue'
import PopArea from '@/custom-component/pop-area/Component.vue'
import PictureGroup from '@/custom-component/picture-group/Component.vue'
import DeScreen from '@/custom-component/de-screen/Component.vue'
import DeDecoration from '@/custom-component/de-decoration/Component.vue'
export const componentsMap = {
VText: VText,
VQuery,
@@ -31,6 +32,7 @@ export const componentsMap = {
DeGraphical: DeGraphical,
CircleShape: CircleShape,
RectShape: RectShape,
DeDecoration: DeDecoration,
SvgTriangle: SvgTriangle,
DeTimeClock: DeTimeClock,
GroupArea: GroupArea,

View File

@@ -9,6 +9,7 @@ import DeScreenAttr from '@/custom-component/de-screen/Attr.vue'
import DeGraphicalAttr from '@/custom-component/de-graphical/Attr.vue'
import CircleShapeAttr from '@/custom-component/circle-shape/Attr.vue'
import RectShapeAttr from '@/custom-component/rect-shape/Attr.vue'
import DeDecorationAttr from '@/custom-component/de-decoration/Attr.vue'
import SvgTriangleAttr from '@/custom-component/svgs/svg-triangle/Attr.vue'
import DeTimeClockAttr from '@/custom-component/de-time-clock/Attr.vue'
import GroupAreaAttr from '@/custom-component/group-area/Attr.vue'
@@ -29,6 +30,7 @@ export const componentsMap = {
DeGraphicalAttr: DeGraphicalAttr,
CircleShapeAttr: CircleShapeAttr,
RectShapeAttr: RectShapeAttr,
DeDecorationAttr: DeDecorationAttr,
SvgTriangleAttr: SvgTriangleAttr,
DeTimeClockAttr: DeTimeClockAttr,
GroupAreaAttr: GroupAreaAttr,