|
|
@@ -1,7 +1,7 @@
|
|
|
<template>
|
|
|
<div :style="styleMap?.mainStyle" class="w-full h-full box-border relative">
|
|
|
<ImageBg :src="styleMap?.mainStyle?.imageSrc" :image-style="styleMap?.mainStyle?.imageStyle" />
|
|
|
- <div class="absolute" :style="{ ...(styleMap?.indicatorStyle || {}), ...otherStyle.barStyle }">
|
|
|
+ <div class="absolute" :style="indicatorStyle">
|
|
|
<ImageBg
|
|
|
:src="styleMap?.indicatorStyle?.imageSrc"
|
|
|
:image-style="styleMap?.indicatorStyle?.imageStyle"
|
|
|
@@ -41,6 +41,7 @@
|
|
|
import { computed } from 'vue'
|
|
|
import { useWidgetStyle } from '../hooks/useWidgetStyle'
|
|
|
import ImageBg from '../ImageBg.vue'
|
|
|
+import type { CSSProperties } from 'vue'
|
|
|
|
|
|
const props = defineProps<{
|
|
|
width?: number
|
|
|
@@ -61,6 +62,44 @@ const styleMap = useWidgetStyle({
|
|
|
props
|
|
|
})
|
|
|
|
|
|
+const isGradientBackground = (background: unknown) => {
|
|
|
+ return typeof background === 'string' && /(?:linear|radial|conic)-gradient\(/.test(background)
|
|
|
+}
|
|
|
+
|
|
|
+const getOffset = (value: string | number | undefined, total = 0) => {
|
|
|
+ if (typeof value === 'number') return value
|
|
|
+ if (!value || value === 'auto') return 0
|
|
|
+ if (value.endsWith('%')) {
|
|
|
+ return (Number(value.replace('%', '')) / 100) * total
|
|
|
+ }
|
|
|
+ if (value.endsWith('px')) {
|
|
|
+ return Number(value.replace('px', ''))
|
|
|
+ }
|
|
|
+ return Number(value) || 0
|
|
|
+}
|
|
|
+
|
|
|
+const indicatorGradientStyle = computed<CSSProperties>(() => {
|
|
|
+ const indicatorBackground = styleMap.value?.indicatorStyle?.background
|
|
|
+ if (!isGradientBackground(indicatorBackground)) return {}
|
|
|
+
|
|
|
+ const width = Number(props.width || 0)
|
|
|
+ const height = Number(props.height || 0)
|
|
|
+ const left = getOffset(otherStyle.value.barStyle.left, width)
|
|
|
+ const top = getOffset(otherStyle.value.barStyle.top, height)
|
|
|
+
|
|
|
+ return {
|
|
|
+ backgroundSize: `${width}px ${height}px`,
|
|
|
+ backgroundPosition: `${-left}px ${-top}px`,
|
|
|
+ backgroundRepeat: 'no-repeat'
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const indicatorStyle = computed(() => ({
|
|
|
+ ...(styleMap.value?.indicatorStyle || {}),
|
|
|
+ ...indicatorGradientStyle.value,
|
|
|
+ ...otherStyle.value.barStyle
|
|
|
+}))
|
|
|
+
|
|
|
const otherStyle = computed(() => {
|
|
|
const { value = 0, max: e = 100, min: s = 0, width = 0, height = 0, mode, startValue } = props
|
|
|
// 不管范围值如何,都取实际最小值和最大值
|
|
|
@@ -147,6 +186,16 @@ const otherStyle = computed(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ const isHorizontal = ['ltr', 'rtl'].includes(direction)
|
|
|
+ const endHorizontalAnchor = (direction === 'ltr') !== reverse ? 'right' : 'left'
|
|
|
+ const startHorizontalAnchor = endHorizontalAnchor === 'right' ? 'left' : 'right'
|
|
|
+ const endVerticalAnchor = (direction === 'btt') !== reverse ? 'top' : 'bottom'
|
|
|
+ const startVerticalAnchor = endVerticalAnchor === 'top' ? 'bottom' : 'top'
|
|
|
+ const getHorizontalKnobTransform = (anchor: 'left' | 'right') =>
|
|
|
+ anchor === 'right' ? 'translateX(50%)' : 'translateX(-50%)'
|
|
|
+ const getVerticalKnobTransform = (anchor: 'top' | 'bottom') =>
|
|
|
+ anchor === 'bottom' ? 'translateY(50%)' : 'translateY(-50%)'
|
|
|
+
|
|
|
return {
|
|
|
barStyle: {
|
|
|
width: width >= height ? 'auto' : '100%',
|
|
|
@@ -158,35 +207,39 @@ const otherStyle = computed(() => {
|
|
|
bottom: e > s ? bottom : top
|
|
|
},
|
|
|
direction,
|
|
|
- endKnobStyle: ['ltr', 'rtl'].includes(direction)
|
|
|
+ endKnobStyle: isHorizontal
|
|
|
? {
|
|
|
top: '-4px',
|
|
|
bottom: 'auto',
|
|
|
- right: direction === 'ltr' && !reverse ? '0' : '',
|
|
|
- left: direction === 'rtl' && !reverse ? '0' : '',
|
|
|
- height: 'calc(100% + 8px)'
|
|
|
+ right: endHorizontalAnchor === 'right' ? '0' : 'auto',
|
|
|
+ left: endHorizontalAnchor === 'left' ? '0' : 'auto',
|
|
|
+ height: 'calc(100% + 8px)',
|
|
|
+ transform: getHorizontalKnobTransform(endHorizontalAnchor)
|
|
|
}
|
|
|
: {
|
|
|
- left: '-4px',
|
|
|
+ left: '50%',
|
|
|
right: 'auto',
|
|
|
- top: direction === 'btt' && !reverse ? '0' : '',
|
|
|
- bottom: direction === 'ttb' && !reverse ? '0' : '',
|
|
|
- width: 'calc(100% + 8px)'
|
|
|
+ top: endVerticalAnchor === 'top' ? '0' : 'auto',
|
|
|
+ bottom: endVerticalAnchor === 'bottom' ? '0' : 'auto',
|
|
|
+ width: 'calc(100% + 8px)',
|
|
|
+ transform: `translateX(-50%) ${getVerticalKnobTransform(endVerticalAnchor)}`
|
|
|
},
|
|
|
- startKnobStyle: ['ltr', 'rtl'].includes(direction)
|
|
|
+ startKnobStyle: isHorizontal
|
|
|
? {
|
|
|
top: '-4px',
|
|
|
bottom: 'auto',
|
|
|
- right: direction === 'ltr' && !reverse ? '' : '0',
|
|
|
- left: direction === 'rtl' && !reverse ? '' : '0',
|
|
|
- height: 'calc(100% + 8px)'
|
|
|
+ right: startHorizontalAnchor === 'right' ? '0' : 'auto',
|
|
|
+ left: startHorizontalAnchor === 'left' ? '0' : 'auto',
|
|
|
+ height: 'calc(100% + 8px)',
|
|
|
+ transform: getHorizontalKnobTransform(startHorizontalAnchor)
|
|
|
}
|
|
|
: {
|
|
|
- left: '-4px',
|
|
|
+ left: '50%',
|
|
|
right: 'auto',
|
|
|
- top: direction === 'btt' && !reverse ? '' : '0',
|
|
|
- bottom: direction === 'ttb' && !reverse ? '' : '0',
|
|
|
- width: 'calc(100% + 8px)'
|
|
|
+ top: startVerticalAnchor === 'top' ? '0' : 'auto',
|
|
|
+ bottom: startVerticalAnchor === 'bottom' ? '0' : 'auto',
|
|
|
+ width: 'calc(100% + 8px)',
|
|
|
+ transform: `translateX(-50%) ${getVerticalKnobTransform(startVerticalAnchor)}`
|
|
|
}
|
|
|
}
|
|
|
})
|