美文网首页
Vue3 & TypeScript组件:刮奖区

Vue3 & TypeScript组件:刮奖区

作者: 牛会骑自行车 | 来源:发表于2023-05-30 16:08 被阅读0次
    效果图

    说明:组件区域只有遮罩那一块,其他内容放页面里;需要先给页面设置一个放刮层的容器,组件在里面会自动撑开。

    思路:

    • 页面正常显示。先设置一个放刮层的容器
    • 组件中也先设置一个容器,容器宽度默认100%,高度也设置为100%
    • onMounted时获取容器宽高,赋值给canvas
    • 设置画布灰色蒙层
    • canvas上设置touchstart和touchmove事件,通过touch事件我们可以获取到当前下手的位置

    点:

    组件代码:

    <script lang='ts' setup>
    
    const { proxy }: any = getCurrentInstance();
    
    const emit = defineEmits(['exceed']);
    // #region init
    type PropsType = {
        // 刮奖区遮罩颜色
        background?: string,
        // 画笔粗细
        lineWidth?: number,
        // 面积设置:大于该值算刮开
        winPercintage?: string
    }
    
    const props = withDefaults(defineProps<PropsType>(), {
        background: '#C0C0C0',
        lineWidth: 8
    })
    
    const context = ref(null);
    const refScratchPrizeDynamic = ref('');
    const canvas = reactive({
        w: 0,
        h: 0
    })
    const canvasOffset = reactive({
        x: 0,
        y: 0
    })
    
    const refScratchPrizeContainer = ref(null);
    const initCanvas = () => {
        // 容器信息
        const containerO = refScratchPrizeContainer.value;
        const {clientWidth, clientHeight} = containerO;
        // 画布宽高撑满容器
        canvas.w = clientWidth;
        canvas.h = clientHeight;
        // 设置灰色蒙层
        nextTick(() => {
            const contextEl = proxy.$refs[refScratchPrizeDynamic.value];
            const {x, y} = contextEl.getBoundingClientRect();
            canvasOffset.x = x;
            canvasOffset.y = y;
            
            const { clientWidth, clientHeight } = contextEl;
            context.value = contextEl.getContext("2d");
    
            context.value.beginPath();
            context.value.fillStyle = props.background;
            context.value.fillRect(0, 0, clientWidth, clientHeight);//设置画布大小
    
            context.value.globalCompositeOperation = "destination-out"; //橡皮擦模式
        })
    }
    
    onMounted(() => {
        refScratchPrizeDynamic.value = proxy.$utils.getUuid(4, { letter: true });
        initCanvas();
    })
    
    // #endregion
    
    // #region main
    const start = (e: TouchEvent) => {
        const { pageX: x, pageY: y } = e.targetTouches[0];
        handleScratch(x, y);
    }
    const count = shallowRef(0);
    const move = (e: TouchEvent) => {
        const { pageX: x, pageY: y } = e.targetTouches[0];
        handleScratch(x, y);
    
        if (props.winPercintage) {
            // 计算刮开面积
            const canvasInfo = context.value.getImageData(0, 0, canvas.w, canvas.h).data;
            const percentage = getUsedArea(canvasInfo);
            
            if (percentage * 100 >= parseInt(props.winPercintage)) {
                if(count.value >= 1) return;
                emit('exceed', percentage * 100 + '%');
                count.value ++;
            }
        }
    }
    
    const handleScratch = (x: number, y: number) => {
        const contextO = context.value;
        contextO.beginPath();
        // 小知识:arc中的x、y是以canvas画布为基准
        contextO.arc(x - canvasOffset.x, y - canvasOffset.y, props.lineWidth, 0, Math.PI * 2);
        contextO.fill();
    }
    
    const getUsedArea = (arr: Uint8ClampedArray) => {
        let used = 0;
        let notUsed = 0;
    
        arr.forEach((item, index) => {
            if (index % 4 === 3) {
                item === 0 && used++
                item === 255 && notUsed++
            }
        })
        return used / (notUsed + used);
    }
    
    // #endregion
    </script>
    
    <template>
        <div class="scratch-prize" ref="refScratchPrizeContainer">
            <canvas :ref="refScratchPrizeDynamic" :width="canvas.w" :height="canvas.h" @touchmove="move" @touchstart="start">
                您的浏览器不支持刮奖操作
            </canvas>
        </div>
    </template>
    
    <style lang="scss" scoped>
    .scratch-prize {
        height: 100%;
    }
    </style>
    

    使用页面代码:

    <script lang='ts' setup>
    const exceed = () => {
        console.log('超出设置百分比')
    }
    </script>
    
    <template>
        <div class="scratch-view">
            <img src="https://img2.baidu.com/it/u=3375776510,3085752136&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1685638800&t=cb4d9fcc048cd4f41227737a607b4098" alt="" class="scratch-view-image">
            <div class="scratch-view-wrap">
                <scratch-prize winPercintage="80%" @exceed="exceed"></scratch-prize>
            </div>
        </div>
    </template>
    
    <style lang="scss" scoped>
    .scratch-view {
        position: relative;
    
        &-wrap {
            width: 150px;
            height: 50px;
            border-radius: 6px;
    
            position: absolute;
            top: 150px;
            left: 50%;
            transform: translateX(-50%);
        }
        &-image {
            width: 100%;
        }
    }
    </style>
    

    tada~一个刮奖组件就完成啦

    相关文章

      网友评论

          本文标题:Vue3 & TypeScript组件:刮奖区

          本文链接:https://www.haomeiwen.com/subject/ygwjedtx.html