| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <template>
- <view class="l-circle" :class="[classes]" :style="[styles]">
- <!-- #ifndef APP-NVUE -->
- <view class="l-circle__trail" :style="[trailStyles]">
- <text class="cap start"></text>
- <text class="cap end"></text>
- </view>
- <view class="l-circle__stroke" :style="[strokeStyles]">
- <text class="cap start" v-if="current"></text>
- <text class="cap end" v-if="current"></text>
- </view>
- <!-- #endif -->
- <!-- #ifdef APP-NVUE -->
- <web-view @pagefinish="finished = true" class="l-circle__view" ref="webview"
- src="/uni_modules/l-circle/hybrid/html/index.html"></web-view>
- <!-- #endif -->
- <view class="l-circle__inner">
- <slot></slot>
- </view>
- </view>
- </template>
- <script lang="ts">
- // @ts-nocheck
- import { ref, computed, onMounted, watch, reactive, defineComponent } from './vue';
- import { toPx, addUnit } from './utils';
- import { useTransition } from './useTransition';
- import CircleProps from './props';
- // import { toPx, addUnit } from '@/uni_modules/lime-ui';
- // import { useTransition } from '@/uni_modules/lime-use';
-
-
- export default defineComponent({
- name: 'l-circle',
- props: CircleProps,
- emits: ['update:current'],
- setup(props, {emit}) {
- const RADIAN = Math.PI / 180
- const ratio = computed(() => 100 / props.max)
- const percent = ref<number>(0)
- const angle = computed(() => props.dashborad ? 135 : -90)
- const isShowCap = computed(() => {
- const { dashborad } = props
- return current.value > 0 && (dashborad ? true : current.value < props.max)
- })
- const offsetTop = ref<number | string>(0)
- const strokeEndCap = reactive({
- x: '0',
- y: '0'
- })
- const styles = computed(() => ({
- width: props.size,
- height: props.size,
- // #ifdef APP-NVUE
- transform: `translateY(${offsetTop.value})`,
- // #endif
- // #ifndef APP-NVUE
- '--l-circle-offset-top': offsetTop.value,
- // #endif
- }))
- const classes = computed(() => {
- const { clockwise, lineCap } = props
- return {
- clockwise,
- [`is-${lineCap}`]: lineCap
- }
- })
- const trailStyles = computed(() => {
- const { size, trailWidth, trailColor, dashborad } = props
- const circle = getCircle(size, trailWidth)
- const mask = `radial-gradient(transparent ${circle.r - 0.5}px, #000 ${circle.r}px)`
- let background = ''
- let capStart = { x: '', y: '' }
- let capEnd = capStart
- if (dashborad) {
- background = `conic-gradient(from 225deg, ${trailColor} 0%, ${trailColor} 75%, transparent 75%, transparent 100%)`
- capStart = calcPosition(circle.c, 135)
- capEnd = calcPosition(circle.c, 45)
- offsetTop.value = (toPx(size) - (toPx(capStart.y) + toPx(trailWidth) / 2)) / 4 + 'px'
- } else {
- background = `${trailColor}`
- }
- return {
- color: trailColor,
- mask,
- '-webkit-mask': mask,
- background,
- '--l-circle-trail-cap-start-x': capStart.x,
- '--l-circle-trail-cap-start-y': capStart.y,
- '--l-circle-trail-cap-end-x': capEnd.x,
- '--l-circle-trail-cap-end-y': capEnd.y,
- // '--l-circle-trail-cap-color': trailColor,
- '--l-circle-trail-cap-size': addUnit(trailWidth)
- }
- })
- const strokeStyles = computed(() => {
- const { size, strokeWidth, strokeColor, duration, dashborad, max } = props
- const circle = getCircle(size, strokeWidth)
- const percent = dashborad ? current.value * 0.75 * ratio.value : current.value * ratio.value;
- const mask = `radial-gradient(transparent ${circle.r - 0.5}px, #000 ${circle.r}px)`
- const cap = calcPosition(circle.c, angle.value)
- let startColor = '';
- let endColor = '';
- let gradient = `conic-gradient(${dashborad ? 'from 225deg,' : ''} transparent 0%,`;
- let gradientEnd = `transparent var(--l-circle-percent), transparent ${dashborad ? '75%' : '100%'})`
- if (typeof strokeColor == 'string') {
- gradient += ` ${strokeColor} 0%, ${strokeColor} var(--l-circle-percent), ${gradientEnd}`
- startColor = endColor = strokeColor
- } else {
- const len = strokeColor.length
- for (let i = 0; i < len; i++) {
- const color = strokeColor[i] as string
- if (i === 0) {
- gradient += `${color} 0%,`
- startColor = color
- } else {
- gradient += `${color} calc(var(--l-circle-percent) * ${(i + 1) / len}),`
- }
- if (i == len - 1) {
- endColor = color
- }
- }
- gradient += gradientEnd
- }
- const a = percent / ratio.value == max ? percent + 0.1 : percent
- return {
- mask,
- '-webkit-mask': mask,
- background: gradient,
- // transition: `--l-circle-percent ${duration}ms`,
- '--l-circle-percent': `${percent / ratio.value == max ? percent + 0.1 : percent}%`,
- '--l-circle-stroke-cap-start-color': startColor,
- '--l-circle-stroke-cap-end-color': endColor,
- '--l-circle-stroke-cap-start-x': cap.x,
- '--l-circle-stroke-cap-start-y': cap.y,
- '--l-circle-stroke-cap-end-x': strokeEndCap.x,
- '--l-circle-stroke-cap-end-y': strokeEndCap.y,
- '--l-circle-stroke-cap-size': addUnit(strokeWidth),
- '--l-circle-stroke-cap-opacity': isShowCap.value ? 1 : 0
- }
- })
- const calcStrokeCap = () => {
- const { size, strokeWidth, dashborad, max } = props
- const circle = getCircle(size, strokeWidth)
- const arc = dashborad ? 180 / 2 * 3 : 180 * 2
- const step = arc / max * current.value + angle.value
- const cap = calcPosition(circle.c, step)
- strokeEndCap.x = cap.x
- strokeEndCap.y = cap.y
- }
- const calcPosition = (r : number, angle : number) => {
- return {
- x: r + r * Math.cos(angle * RADIAN) + 'px',
- y: r + r * Math.sin(angle * RADIAN) + 'px'
- }
- }
- const getCircle = (size : number | string, lineWidth : number | string) => {
- const s = toPx(size)
- const w = toPx(lineWidth)
- const c = (s - w) / 2
- const r = s / 2 - w
- return {
- s, w, c, r
- }
- }
-
- const current = useTransition(percent, {
- duration: props.duration,
- })
- watch(() => props.percent, () => {
- percent.value = props.percent
- }, { immediate: true })
- watch(current, (v) => {
- calcStrokeCap()
- emit('update:current', v.toFixed(2))
- })
- // #ifdef APP-NVUE
- const finished = ref(false)
- const webview = ref(null)
- watch(finished, () => {
- webview.value.evalJs(`setClass('.l-circle', 'is-round', ${props.lineCap == 'round'})`)
- webview.value.evalJs(`setClass('.l-circle', 'clockwise', ${props.clockwise})`)
- watch([trailStyles, strokeStyles], (v) => {
- webview.value.evalJs(`setStyle(0,${JSON.stringify(v[0])})`)
- webview.value.evalJs(`setStyle(1,${JSON.stringify(v[1])})`)
- }, { immediate: true })
- })
- // #endif
-
- return {
- classes,
- styles,
- trailStyles,
- strokeStyles,
- current,
- // #ifdef APP-NVUE
- finished
- // #endif
- }
- }
- })
- </script>
- <style lang="scss">
- @import './index';
- </style>
|