260 lines
7.6 KiB
Plaintext
260 lines
7.6 KiB
Plaintext
<template>
|
|
<view class="l-dialer" :style="rootStyles">
|
|
<view class="l-dialer__inner" :style="innerStyle">
|
|
<view class="l-dialer__inner-border" v-if="$slots['border'] != null">
|
|
<slot name="border" />
|
|
</view>
|
|
<view class="l-dialer__inner-wrap" ref="drawbleRef" :style="wrapStyle">
|
|
<view class="l-dialer__inner-item" v-for="(item, index) in prizeList" :key="index"
|
|
:style="itemStyle(index)">
|
|
<view class="l-dialer__inner-content" :style="contentStyle(index)">
|
|
<slot v-if="$slots['prize'] != null" name="prize" :item="item" :even="index % 2"></slot>
|
|
<template v-else>
|
|
<text class="l-dialer__inner-name" :style="nameStyle">{{ item['name'] }}</text>
|
|
<image class="l-dialer__inner-img" :src="item['img']"></image>
|
|
</template>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="l-dialer__pointer" :style="pointerStyle">
|
|
<slot v-if="$slots['pointer'] != null" name="pointer" />
|
|
<image v-else :class="!isTurnIng ? 'heart' : ''" src="/uni_modules/lime-dialer/static/turnable_btn.png"
|
|
style="width: 100%" mode="widthFix" @tap="$emit('click')" />
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<script lang="uts" setup>
|
|
const emits = defineEmits(['click', 'done'])
|
|
const slots = defineSlots<{
|
|
prize : {
|
|
item : UTSJSONObject,
|
|
even : number
|
|
}
|
|
}>()
|
|
const props = defineProps({
|
|
size: {
|
|
// #ifdef APP-ANDROID
|
|
type: Object,
|
|
// #endif
|
|
// #ifndef APP-ANDROID
|
|
type: [String, Number],
|
|
// #endif
|
|
default: 300
|
|
},
|
|
prizeList: {
|
|
type: Array as PropType<UTSJSONObject[]>,
|
|
default: () : UTSJSONObject[] => []
|
|
},
|
|
turns: {
|
|
type: Number,
|
|
default: 10
|
|
},
|
|
duration: {
|
|
type: Number,
|
|
default: 3
|
|
},
|
|
styleOpt: {
|
|
type: Object as PropType<UTSJSONObject>,
|
|
default: () : UTSJSONObject => ({
|
|
// 每一块扇形的背景色,默认值,可通过父组件来改变
|
|
prizeBgColors: ['#fff0a3', '#fffce6'],
|
|
// 每一块扇形的外边框颜色,默认值,可通过父组件来改变
|
|
borderColor: '#ffd752',
|
|
} as UTSJSONObject)
|
|
},
|
|
customStyle: {
|
|
type: String,
|
|
},
|
|
dialStyle: {
|
|
type: String,
|
|
},
|
|
pointerStyle: {
|
|
type: String,
|
|
default: `width: 30%`
|
|
}
|
|
})
|
|
|
|
const drawbleRef = ref<UniElement | null>(null)
|
|
const startRotateDegree = ref(0)
|
|
const rotateAngle = ref('rotate(0deg)')
|
|
const rotateTransition = ref('')
|
|
const isTurnIng = ref(false)
|
|
|
|
|
|
const getStyleOpt = computed(() : UTSJSONObject => {
|
|
const style = {
|
|
// 每一块扇形的背景色,默认值,可通过父组件来改变
|
|
prizeBgColors: ['#fff0a3', '#fffce6'],
|
|
// 每一块扇形的外边框颜色,默认值,可通过父组件来改变
|
|
borderColor: '#ffd752',
|
|
}
|
|
return UTSJSONObject.assign(style, props.styleOpt)
|
|
})
|
|
const rootStyles = computed(() : string => {
|
|
const size = /\d$/.test(`${props.size}`) ? `${props.size}px` : props.size;
|
|
return `width: ${size}; height: ${size}; ${props.customStyle}`
|
|
})
|
|
|
|
const innerStyle = computed(() : string => {
|
|
// const style = new Map<string, string>()
|
|
let style = ''
|
|
const padding = getStyleOpt.value['padding'] ?? 0
|
|
|
|
style += `padding: ${padding};`
|
|
style += `transform: ${rotateAngle.value};`
|
|
style += `${rotateTransition.value};`//`transition: ${rotateTransition.value};`
|
|
style += `${props.dialStyle};`
|
|
|
|
return style
|
|
})
|
|
|
|
const wrapStyle = computed(() : string => {
|
|
const borderColor = getStyleOpt.value['borderColor']
|
|
if (borderColor != null) {
|
|
return `border: 1rpx solid ${borderColor}`
|
|
}
|
|
return ''
|
|
})
|
|
|
|
const itemStyle = computed(() : ((index : number) => Map<string, any>) => {
|
|
return (index : number) : Map<string, any> => {
|
|
const length = props.prizeList.length;
|
|
const prizeBgColors : string[] = (getStyleOpt.value['prizeBgColors'] ?? [] as string[]) as string[]
|
|
const prizeBgColorsLength = prizeBgColors.length;
|
|
const borderColor = getStyleOpt.value['borderColor']
|
|
const style = new Map<string, any>();
|
|
// #ifndef APP
|
|
if (length == 2) {
|
|
// style['transform'] = index == 0 ? 0 : `rotate(270deg)`
|
|
style.set('transform', index == 0 ? `rotate(0deg)` : `rotate(270deg)`)
|
|
style.set('top', 0)
|
|
} else {
|
|
style.set('transform', `rotate(${(360 / length) * index}deg) skewX(0deg) skewY(${360 / length - 90}deg)`);
|
|
}
|
|
if (prizeBgColorsLength > 0) {
|
|
style.set('backgroundColor', `${prizeBgColors[index % prizeBgColorsLength]}`)
|
|
}
|
|
if (borderColor != null) {
|
|
style.set('border', `1rpx solid ${borderColor}`)
|
|
}
|
|
// #endif
|
|
// #ifdef APP
|
|
if (length == 2) {
|
|
style.set('backgroundColor', `${prizeBgColors[index % prizeBgColorsLength]}`)
|
|
style.set('transform', index == 0 ? `rotate(0deg)` : `rotate(270deg)`);
|
|
style.set('top', 0)
|
|
if (borderColor != null) {
|
|
style.set('border', `1rpx solid ${borderColor}`)
|
|
}
|
|
} else {
|
|
style.set('transform', `rotate(${(360 / length) * index}deg)`);
|
|
}
|
|
|
|
// #endif
|
|
return style
|
|
}
|
|
})
|
|
|
|
const contentStyle = computed(() : ((index : number) => string) => {
|
|
return (index : number) : string => {
|
|
// #ifndef APP
|
|
if (props.prizeList.length != 2) {
|
|
return `transform: skewY(${90 - 360 / props.prizeList.length}deg) skewX(0deg) rotate(${180 / props.prizeList.length}deg)`
|
|
} else {
|
|
return index == 0
|
|
? `transform: rotate(90deg); bottom: 0`
|
|
: `transform: rotate(0deg); bottom: -50%; left: 0`
|
|
}
|
|
|
|
// #endif
|
|
// #ifdef APP
|
|
if (props.prizeList.length != 2) {
|
|
return `transform: rotate(${180 / props.prizeList.length}deg)`
|
|
} else {
|
|
return index == 0
|
|
? `transform: rotate(90deg); bottom: 0`
|
|
: `transform: rotate(0deg); bottom: -50%; left: 0`
|
|
}
|
|
|
|
// #endif
|
|
}
|
|
|
|
})
|
|
|
|
const nameStyle = computed(() : Map<string, any> => {
|
|
const fontSize = getStyleOpt.value['fontSize']
|
|
const color = getStyleOpt.value['color']
|
|
const style = new Map<string, any>()
|
|
|
|
if (fontSize != null) {
|
|
style.set('fontSize', fontSize)
|
|
}
|
|
if (color != null) {
|
|
style.set('color', color)
|
|
}
|
|
return style
|
|
})
|
|
|
|
|
|
const run = (index : number) => {
|
|
if (isTurnIng.value) return
|
|
const duration = props.duration;
|
|
const length = props.prizeList.length;
|
|
|
|
const _rotateAngle = startRotateDegree.value + props.turns * 360 + 360 - (180 / length + (360 / length) * index) - (startRotateDegree.value % 360);
|
|
startRotateDegree.value = _rotateAngle;
|
|
rotateAngle.value = `rotate(${_rotateAngle}deg)`;
|
|
rotateTransition.value = `transition-duration: ${duration}s`;
|
|
isTurnIng.value = true
|
|
setTimeout(() => {
|
|
emits('done', index);
|
|
isTurnIng.value = false
|
|
}, duration * 1000 + 500);
|
|
}
|
|
// #ifdef APP
|
|
onMounted(() => {
|
|
nextTick(() => {
|
|
if (drawbleRef.value == null) return;
|
|
const ctx = drawbleRef.value!.getDrawableContext()!;
|
|
const size = drawbleRef.value!.offsetWidth;
|
|
watch(props.prizeList, () => {
|
|
ctx.reset()
|
|
const length = props.prizeList.length;
|
|
if (length == 2) return
|
|
const prizeBgColors : string[] = (getStyleOpt.value['prizeBgColors'] ?? [] as string[]) as string[]
|
|
const prizeBgColorsLength = prizeBgColors.length;
|
|
const borderColor = getStyleOpt.value['borderColor'] as string | null
|
|
|
|
const centerX = size / 2;
|
|
const centerY = size / 2;
|
|
const radius = size / 2;
|
|
|
|
const angle = (2 * Math.PI) / length;
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(centerX, centerY);
|
|
ctx.arc(centerX, centerY, radius, i * angle, (i + 1) * angle);
|
|
ctx.lineTo(centerX, centerY);
|
|
ctx.closePath();
|
|
ctx.fillStyle = prizeBgColors[i % prizeBgColorsLength];
|
|
if (borderColor != null) {
|
|
ctx.lineWidth = 2
|
|
ctx.strokeStyle = borderColor;
|
|
ctx.stroke()
|
|
}
|
|
ctx.fill();
|
|
}
|
|
ctx.update()
|
|
}, { immediate: true })
|
|
})
|
|
})
|
|
// #endif
|
|
defineExpose({
|
|
run
|
|
})
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
@import './index';
|
|
</style> |