神缘叔
神缘叔
  • 发布:2021-03-17 16:04
  • 更新:2021-03-17 16:04
  • 阅读:507

那位高手可以帮忙解决下canvas的问题 滑动验证码

分类:uni-app

滑动的时候不显示滑动块

<template>  
    <div class="yylp-slide-verify" :style="'width:' + w + 'px'" id="slideVerify" onselectstart="return false;">  
        <!-- 图片加载遮蔽罩 -->  
        <div :class="{ 'slider-verify-loading': loadBlock }"></div>  
        <canvas :style="{ width: w + 'px', height: h + 'px' }" type="2d" canvas-id="yylp-slide-canvas" ref="canvas"></canvas>  
        <div v-if="show" @click="refresh" class="slide-verify-refresh-icon"><text class="refresh_icon yylp yylp-icon_refresh"></text></div>  
        <canvas :style="{ width: w + 'px', height: h + 'px' }" type="2d" canvas-id="yylp-slide-block" ref="block" class="slide-verify-block"></canvas>  
        <!-- container -->  
        <div class="slide-verify-slider" :class="{ 'container-active': containerActive, 'container-success': containerSuccess, 'container-fail': containerFail }">  
            <div class="slide-verify-slider-mask" :style="{ width: sliderMaskWidth }">  
                <!-- slider -->  
                <div  
                    @mousedown="sliderDown"  
                    @touchstart="touchStartEvent"  
                    @touchmove="touchMoveEvent"  
                    @touchend="touchEndEvent"  
                    class="slide-verify-slider-mask-item"  
                    :style="{ left: sliderLeft }"  
                >  
                    <text  
                        class="slide_icon yylp"  
                        :class="{ 'yylp-you-2': !containerSuccess && !containerFail, 'yylp-': containerSuccess, 'yylp-cuo': containerFail }"  
                    ></text>  
                </div>  
            </div>  
            <span class="slide-verify-slider-text">{{ sliderText }}</span>  
        </div>  
    </div>  
</template>  
<script>  
const PI = Math.PI;  

function sum(x, y) {  
    return x + y;  
}  

function square(x) {  
    return x * x;  
}  
export default {  
    name: 'SlideVerify',  
    props: {  
        // block length  
        l: {  
            type: Number,  
            default: 42  
        },  
        // block radius  
        r: {  
            type: Number,  
            default: 10  
        },  
        // canvas width  
        w: {  
            type: Number,  
            default: 310  
        },  
        // canvas height  
        h: {  
            type: Number,  
            default: 155  
        },  
        sliderText: {  
            type: String,  
            default: '向右滑动>'  
        },  
        accuracy: {  
            type: Number,  
            default: 5 // 若为 -1 则不进行机器判断  
        },  
        show: {  
            type: Boolean,  
            default: true  
        },  
        imgs: {  
            type: Array,  
            default: () => ['img0.jpg', 'img1.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg']  
        }  
    },  
    data() {  
        return {  
            containerActive: false, // container active class  
            containerSuccess: false, // container success class  
            containerFail: false, // container fail class  
            canvasCtx: null,  
            blockCtx: null,  
            block: null,  
            block_x: undefined, // container random position  
            block_y: undefined,  
            L: this.l + this.r * 2 + 3, // block real lenght  
            img: undefined,  
            originX: undefined,  
            originY: undefined,  
            isMouseDown: false,  
            trail: [],  
            sliderLeft: 0, // block right offset  
            sliderMaskWidth: 0, // mask width,  
            success: false, // Bug Fixes 修复了验证成功后还能滑动  
            loadBlock: true, // Features 图片加载提示,防止图片没加载完就开始验证  
            timestamp: null  
        };  
    },  
    mounted() {  
        this.init();  
    },  
    methods: {  
        init() {  
            this.initDom();  
            setTimeout(() => {  
                this.initImg();  
                this.bindEvents();  
            }, 500);  
        },  
        initDom() {  
            this.block = this.$refs.block.$el;  
            this.canvasCtx = uni.createCanvasContext('yylp-slide-canvas', this);  
            this.blockCtx = uni.createCanvasContext('yylp-slide-block', this);  
        },  
        initImg() {  
            let that = this;  
            const img = that.createImg(() => {  
                // 图片加载完关闭遮蔽罩  
                that.loadBlock = false;  
                that.drawBlock();  
                that.canvasCtx.drawImage(that.img.src, 0, 0, that.w, that.h);  
                that.blockCtx.drawImage(that.img.src, 0, 0, that.w, that.h);  
                that.canvasCtx.draw();  
                that.blockCtx.draw();  
                let { block_x: x, block_y: y, r, L } = that;  
                let _y = y - r * 2 - 1;  

                that.$nextTick(function() {  
                    uni.canvasGetImageData(  
                        {  
                            canvasId: 'yylp-slide-block',  
                            x: x,  
                            y: _y,  
                            width: L,  
                            height: L,  
                            success(ImageData) {  
                                console.log('复制图片成功');  
                                that.block.style.width = L + 'px';  
                                uni.canvasPutImageData(  
                                    {  
                                        canvasId: 'yylp-slide-block',  
                                        x: 0,  
                                        y: _y,  
                                        data: ImageData.data,  
                                        width: L,  
                                        success(res) {  
                                            console.log('设置图片成功', res);  
                                        },  
                                        fail(err) {  
                                            console.log('设置图片错误', err);  
                                        }  
                                    },  
                                    that  
                                );  
                            },  
                            fail(err) {  
                                console.log('复制图片错误', err);  
                            }  
                        },  
                        that  
                    );  
                });  
            });  
            that.img = img;  
        },  
        drawBlock() {  
            this.block_x = this.getRandomNumberByRange(this.L + 10, this.w - (this.L + 10));  
            this.block_y = this.getRandomNumberByRange(10 + this.r * 2, this.h - (this.L + 10));  
            this.draw(this.canvasCtx, this.block_x, this.block_y, 'fill');  
            this.draw(this.blockCtx, this.block_x, this.block_y, 'clip');  
        },  
        draw(ctx, x, y, operation) {  
            let { l, r } = this;  
            ctx.beginPath();  
            ctx.moveTo(x, y);  
            ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI);  
            ctx.lineTo(x + l, y);  
            ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI);  
            ctx.lineTo(x + l, y + l);  
            ctx.lineTo(x, y + l);  
            ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true);  
            ctx.lineTo(x, y);  
            ctx.lineWidth = 2;  
            ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';  
            ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';  
            ctx.stroke();  
            ctx[operation]();  
            // Bug Fixes 修复了火狐和ie显示问题  
            ctx.globalCompositeOperation = 'destination-over';  
        },  
        createImg(onload) {  
            const img = document.createElement('img');  
            img.crossOrigin = 'Anonymous';  
            img.onload = onload;  
            img.onerror = () => {  
                img.src = this.getRandomImg();  
            };  
            img.src = this.getRandomImg();  
            return img;  
        },  
        // 随机生成img src  
        getRandomImg() {  
            const len = this.imgs.length;  
            const img_src = this.imgs[this.getRandomNumberByRange(0, len - 1)];  
            if (img_src) {  
                return require('@/uni_modules/yylp-ui/static/images/slide_verify/' + img_src);  
            }  
        },  
        getRandomNumberByRange(start, end) {  
            return Math.round(Math.random() * (end - start) + start);  
        },  
        refresh() {  
            console.log('刷新验证码!');  
            this.reset();  
            this.$emit('action_change', { action: 'refresh', msg: '刷新校验' });  
        },  
        sliderDown(event) {  
            if (this.success) return;  
            this.originX = event.clientX;  
            this.originY = event.clientY;  
            this.isMouseDown = true;  
            this.timestamp = +new Date();  
        },  
        touchStartEvent(e) {  
            if (this.success) return;  
            this.originX = e.changedTouches[0].pageX;  
            this.originY = e.changedTouches[0].pageY;  
            this.isMouseDown = true;  
            this.timestamp = +new Date();  
        },  
        bindEvents() {  
            document.addEventListener('mousemove', e => {  
                if (!this.isMouseDown) return false;  
                const moveX = e.clientX - this.originX;  
                const moveY = e.clientY - this.originY;  
                if (moveX < 0 || moveX + 38 >= this.w) return false;  
                this.sliderLeft = moveX + 'px';  
                let blockLeft = ((this.w - 40 - 20) / (this.w - 40)) * moveX;  
                this.block.style.left = blockLeft + 'px';  

                this.containerActive = true; // add active  
                this.sliderMaskWidth = moveX + 'px';  
                this.trail.push(moveY);  
            });  
            document.addEventListener('mouseup', e => {  
                if (!this.isMouseDown) return false;  
                this.isMouseDown = false;  
                if (e.clientX === this.originX) return false;  
                this.containerActive = false; // remove active  
                this.timestamp = +new Date() - this.timestamp;  

                const { spliced, TuringTest } = this.verify();  
                if (spliced) {  
                    if (this.accuracy === -1) {  
                        this.containerSuccess = true;  
                        this.success = true;  
                        this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });  
                        return;  
                    }  
                    if (TuringTest) {  
                        // succ  
                        this.containerSuccess = true;  
                        this.success = true;  
                        this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });  
                    } else {  
                        this.containerFail = true;  
                        this.$emit('action_change', { action: 'again', msg: '检测到非人为操作的' });  
                    }  
                } else {  
                    this.containerFail = true;  
                    this.$emit('action_change', { action: 'fail', msg: '校验失败!' });  
                    setTimeout(() => {  
                        this.reset();  
                    }, 1000);  
                }  
            });  
        },  
        touchMoveEvent(e) {  
            if (!this.isMouseDown) return false;  
            const moveX = e.changedTouches[0].pageX - this.originX;  
            const moveY = e.changedTouches[0].pageY - this.originY;  
            if (moveX < 0 || moveX + 38 >= this.w) return false;  
            this.sliderLeft = moveX + 'px';  
            let blockLeft = ((this.w - 40 - 20) / (this.w - 40)) * moveX;  
            this.block.style.left = blockLeft + 'px';  

            this.containerActive = true;  
            this.sliderMaskWidth = moveX + 'px';  
            this.trail.push(moveY);  
        },  
        touchEndEvent(e) {  
            if (!this.isMouseDown) return false;  
            this.isMouseDown = false;  
            if (e.changedTouches[0].pageX === this.originX) return false;  
            this.containerActive = false;  
            this.timestamp = +new Date() - this.timestamp;  

            const { spliced, TuringTest } = this.verify();  
            if (spliced) {  
                if (this.accuracy === -1) {  
                    this.containerSuccess = true;  
                    this.success = true;  
                    this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });  
                    return;  
                }  
                if (TuringTest) {  
                    // succ  
                    this.containerSuccess = true;  
                    this.success = true;  
                    this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });  
                } else {  
                    this.containerFail = true;  
                    this.$emit('action_change', { action: 'again', msg: '检测到非人为操作的' });  
                }  
            } else {  
                this.containerFail = true;  
                this.$emit('action_change', { action: 'fail', msg: '校验失败!' });  
                setTimeout(() => {  
                    this.reset();  
                }, 1000);  
            }  
        },  
        verify() {  
            const arr = this.trail; // drag y move distance  
            const average = arr.reduce(sum) / arr.length; // average  
            const deviations = arr.map(x => x - average); // deviation array  
            const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length); // standard deviation  
            const left = parseInt(this.block.style.left);  
            const accuracy = this.accuracy <= 1 ? 1 : this.accuracy > 10 ? 10 : this.accuracy;  
            return {  
                spliced: Math.abs(left - this.block_x) <= accuracy,  
                TuringTest: average !== stddev // equal => not person operate  
            };  
        },  
        reset() {  
            this.success = false;  
            this.containerActive = false;  
            this.containerSuccess = false;  
            this.containerFail = false;  
            this.sliderLeft = 0;  
            this.block.style.left = 0;  
            this.sliderMaskWidth = 0;  
            // canvas  
            let { w, h } = this;  
            this.canvasCtx.clearRect(0, 0, w, h);  
            this.blockCtx.clearRect(0, 0, w, h);  
            this.block.style.width = w + 'px';  
            this.canvasCtx.draw();  
            this.blockCtx.draw();  
            // generate img  
            this.img.src = this.getRandomImg();  
            this.$emit('action_change', { action: 'fulfilled', msg: '刷新校验完成' });  
        }  
    }  
};  
</script>  
<style lang="scss">  
.yylp-slide-verify {  
    position: relative;  
    margin: 0rpx auto;  
    background-color: #ffffff;  

    /* 图片加载样式 */  
    .slider-verify-loading {  
        position: absolute;  
        top: 0;  
        right: 0;  
        left: 0;  
        bottom: 0;  
        background: rgba(255, 255, 255, 0.9);  
        z-index: 999;  
        animation: loading 1.5s infinite;  
    }  
    @keyframes loading {  
        0% {  
            opacity: 0.7;  
        }  
        100% {  
            opacity: 9;  
        }  
    }  

    .slide-verify-block {  
        position: absolute;  
        left: 0;  
        top: 0;  
    }  

    .slide-verify-refresh-icon {  
        position: absolute;  
        right: 10rpx;  
        top: 10rpx;  
        z-index: 2;  
        .refresh_icon {  
            font-size: 56rpx;  
            color: #666666;  
            text-shadow: 2rpx 2rpx 2rpx #ffffff;  
        }  
    }  

    .slide-verify-slider {  
        position: relative;  
        text-align: center;  
        width: 100%;  
        height: 40px;  
        line-height: 40px;  
        margin-top: 15px;  
        background: #f7f9fa;  
        color: #45494c;  
        border: 1px solid #e4e7eb;  
        .slide_icon {  
            font-size: 56rpx;  
        }  
    }  

    .slide-verify-slider-mask {  
        position: absolute;  
        left: 0;  
        top: 0;  
        height: 40px;  
        border: 0 solid #1991fa;  
        background: #d1e9fe;  
    }  

    .slide-verify-slider-mask-item {  
        position: absolute;  
        top: 0;  
        left: 0;  
        width: 40px;  
        height: 40px;  
        background: #fff;  
        box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);  
        cursor: pointer;  
        transition: background 0.2s linear;  
    }  

    .slide-verify-slider-mask-item:hover {  
        background-color: #1991fa;  
        .slide_icon {  
            color: #ffffff;  
        }  
    }  

    .container-active {  
        .slide-verify-slider-mask-item {  
            height: 38px;  
            top: -1px;  
            border: 1px solid #1991fa;  
        }  
        .slide-verify-slider-mask {  
            height: 38px;  
            border-width: 1px;  
        }  
    }  

    .container-success {  
        .slide-verify-slider-mask-item {  
            height: 38px;  
            top: -1px;  
            border: 1px solid #52ccba;  
            background-color: #52ccba !important;  
        }  
        .slide-verify-slider-mask {  
            height: 38px;  
            border: 1px solid #52ccba;  
            background-color: #d2f4ef;  
        }  
        .slide_icon {  
            color: #ffffff;  
        }  
    }  

    .container-fail {  
        .slide-verify-slider-mask-item {  
            height: 38px;  
            top: -1px;  
            border: 1px solid #f57a7a;  
            background-color: #f57a7a !important;  
        }  
        .slide-verify-slider-mask {  
            height: 38px;  
            border: 1px solid #f57a7a;  
            background-color: #fce1e1;  
        }  
        .slide_icon {  
            color: #ffffff;  
        }  
    }  

    .container-active .slide-verify-slider-text,  
    .container-success .slide-verify-slider-text,  
    .container-fail .slide-verify-slider-text {  
        display: none;  
    }  
}  
</style>  
2021-03-17 16:04 负责人:无 分享
已邀请:

该问题目前已经被锁定, 无法添加新回复