搜索

首页  >  问答  >  正文

javascript - 急求关于canvas问题

今天做一个刮奖的项目,要用到canvas,在用户进行刮奖时,从后台请求数据并且用canvas将奖品名字画在画布上,但是问题来了,用户在进行刮奖操作时,从后台请求的数据要画在画布上,好像是需要重新绘制图形,但是那样用户第一步刮奖的操作就无效了,请问是否能够只更新奖品名字那一部分?或者有其他更好的办法?
附代码:

$(function() {
//定义Lottery类
    function Lottery(id, cover, coverType, width, height, drawPercentCallback) {
        this.conId = id;
        this.conNode = document.getElementById(this.conId);
        this.cover = cover;
        this.coverType = coverType;
        this.background = null;
        this.backCtx = null;
        this.mask = null;
        this.maskCtx = null;
        this.lottery = null;
        this.lotteryType = 'image';
        this.width = width || 800;
        this.height = height || 100;
        this.clientRect = null;
        this.drawPercentCallback = drawPercentCallback;
    }

    Lottery.prototype = {
        createElement: function (tagName, attributes) {
            var ele = document.createElement(tagName);
            for (var key in attributes) {
                ele.setAttribute(key, attributes[key]);
            }
            return ele;
        },
        //涂抹区域百分比
        getTransparentPercent: function (ctx, width, height) {
            var imgData = ctx.getImageData(0, 0, width, height),
                pixles = imgData.data,
                transPixs = [];
            for (var i = 0, j = pixles.length; i < j; i += 4) {
                var a = pixles[i + 3];
                if (a < 128) {
                    transPixs.push(i);
                }
            }
            return (transPixs.length / (pixles.length / 4) * 100).toFixed(2);
        },
        //改变canvas大小的工具方法。
        resizeCanvas: function (canvas, width, height) {
            canvas.width = width;
            canvas.height = height;
            canvas.getContext('2d').clearRect(0, 0, width, height);
        },
        //绘制点击和涂抹区域
        drawPoint: function (x, y) {
            this.maskCtx.beginPath();
            var radgrad = this.maskCtx.createRadialGradient(x, y, 0, x, y, 30);
            radgrad.addColorStop(0, 'rgba(0,0,0,0.6)');
            radgrad.addColorStop(1, 'rgba(255, 255, 255, 0)');
            this.maskCtx.fillStyle = radgrad;
            this.maskCtx.arc(x, y, 15, 0, Math.PI * 2, true);
            this.maskCtx.fill();
            if (this.drawPercentCallback) {
                this.drawPercentCallback.call(null, this.getTransparentPercent(this.maskCtx, this.width, this.height));
            }
        },
        //绑定事件
        bindEvent: function () {
            var _this = this;
            var device = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
            var clickEvtName = device ? 'touchstart' : 'mousedown';
            var moveEvtName = device ? 'touchmove' : 'mousemove';
            if (!device) {
                var isMouseDown = false;
                document.addEventListener('mouseup', function (e) {
                    isMouseDown = false;
                }, false);
            } else {
                document.addEventListener("touchmove", function (e) {
                    if (isMouseDown) {
                        e.preventDefault();
                    }
                }, false);
                document.addEventListener('touchend', function (e) {
                    isMouseDown = false;
                }, false);
            }
            this.mask.addEventListener(clickEvtName, function (e) {
                isMouseDown = true;
                var docEle = document.documentElement;
                if (!_this.clientRect) {
                    _this.clientRect = {
                        left: 0,
                        top: 0
                    };
                }
                var x = (device ? e.touches[0].clientX : e.clientX) - _this.clientRect.left + docEle.scrollLeft - docEle.clientLeft;
                var y = (device ? e.touches[0].clientY : e.clientY) - _this.clientRect.top + docEle.scrollTop - docEle.clientTop;
                _this.drawPoint(x, y);
            }, false);

            this.mask.addEventListener(moveEvtName, function (e) {
                if (!device && !isMouseDown) {
                    return false;
                }
                var docEle = document.documentElement;
                if (!_this.clientRect) {
                    _this.clientRect = {
                        left: 0,
                        top: 0
                    };
                }
                var x = (device ? e.touches[0].clientX : e.clientX) - _this.clientRect.left + docEle.scrollLeft - docEle.clientLeft;
                var y = (device ? e.touches[0].clientY : e.clientY) - _this.clientRect.top + docEle.scrollTop - docEle.clientTop;
                _this.drawPoint(x, y);
            }, false);
        },
        //添加两个canvas到刮奖容器,并获取2d上下文
        drawLottery: function () {
            this.background = this.background || this.createElement('canvas', {
                    style: 'position:absolute;left:0;top:0;'
                });
            this.mask = this.mask || this.createElement('canvas', {
                    style: 'position:absolute;left:0;top:0;'
                });

            if (!this.conNode.innerHTML.replace(/[\w\W]| /g, '')) {
                this.conNode.appendChild(this.background);
                this.conNode.appendChild(this.mask);
                this.clientRect = this.conNode ? this.conNode.getBoundingClientRect() : null;
                this.bindEvent();
            }

            this.backCtx = this.backCtx || this.background.getContext('2d');
            this.maskCtx = this.maskCtx || this.mask.getContext('2d');
            //绘制第一个canvas
            if (this.lotteryType == 'image') {
                var image = new Image(),
                    _this = this;
                image.onload = function () {
                    _this.width = this.width;
                    _this.height = this.height;
                    _this.resizeCanvas(_this.background, this.width, this.height);
                    _this.backCtx.drawImage(this, 0, 0);
                    _this.drawMask();
                }
                image.src = this.lottery;
            } else if (this.lotteryType == 'text') {
                this.width = this.width;
                this.height = this.height;
                this.resizeCanvas(this.background, this.width, this.height);
                this.backCtx.save();
                this.backCtx.fillStyle = '#FFF';
                this.backCtx.fillRect(0, 0, this.width, this.height);
                this.backCtx.restore();
                this.backCtx.save();
                var fontSize = 20;
                this.backCtx.font = 'Bold ' + fontSize + 'px Arial';
                this.backCtx.textAlign = 'center';
                this.backCtx.fillStyle = '#da345f';
                this.backCtx.fillText(this.lottery, this.width / 2, this.height / 2 + fontSize / 2);
                this.backCtx.restore();
                this.drawMask();
            }
        },
        //绘制第二个canvas
        drawMask: function () {
            this.resizeCanvas(this.mask, this.width, this.height);
            if (this.coverType == 'color') {
                this.maskCtx.fillStyle = this.cover;
                this.maskCtx.fillRect(0, 0, this.width, this.height);
                this.maskCtx.globalCompositeOperation = 'destination-out';
            } else if (this.coverType == 'image') {
                var image = new Image(),
                    _this = this;
                image.onload = function () {
                    _this.maskCtx.drawImage(this, 0, 0);
                    _this.maskCtx.globalCompositeOperation = 'destination-out';
                }
                image.src = this.cover;
            }
        },
        //调用入口init
        init: function (lottery, lotteryType) {
            this.lottery = lottery;
            this.lotteryType = lotteryType || 'image';
            this.drawLottery();
        }
    };
    //加载页面绘制空白数据
    window.onload = function () {
        var lottery = new Lottery('scratch-area', '#092f57', 'color', 300, 102,drawPercent);
        lottery.init("",'text');
        //判断刮开的区域
        function drawPercent(percent) {
            $("#drawPercent").html(percent);
            var area = $("#drawPercent").html();
            //console.log(area);
        }
        //开始刮奖请求数据,并重新绘制canvas
        $("#scratch-area").data("flag", true).on("touchstart", function (e) {
            e.preventDefault();
            if ($(this).data("flag")) {
                $.ajax({
                    url: '/scratchcard/lottery?eventId=' + eventId + '&mappId=' + appId,
                    dataType: 'json',
                    success: function (data) {
                        console.log(data);
                        $(".frequency-total").text(data.prizeChance);
                        var prize = data.prizeName;
                        //抽奖次数
                        var prizeChance = data.prizeChance;
                        //是否抽中奖品
                        var prizeType = parseInt(data.prizeType);
                        //判断抽奖机会
                        if (prizeChance > 0) {
                            var lottery = new Lottery('scratch-area', '#092f57', 'color', 300, 102, drawPercent);
                            lottery.init(prize, 'text');
                            if (prizeType == 0) {
                                $(".look-prize").css("visibility","visible").on("click",function(){
                                    window.location.href="/center/coupon?mappId="+mappId;
                                });
                            }
                            function drawPercent(percent) {
                                $("#drawPercent").html(percent);
                                var area = $("#drawPercent").html();
                                if (area > 9.5 && prizeType == 2) {
                                    //app.alert('恭喜您获得' + '"' + prize + '"!', '刮刮乐', function () {
                                    //    //location.reload();
                                    //    console.log("in")
                                    //});
                                    $(".look-prize").css("visibility","visible").html("点我刷新").on("click",function(){
                                       location.reload();
                                    });
                                }
                            }

                        } else {
                            app.alert('您的剩余刮奖次数不足!', '刮刮乐',function(){
                                location.reload();
                            });
                        }

                    },
                    error: function (error) {
                        app.alert(error.responseText, '刮刮乐');
                    }
                });
                $(this).data("flag", false);
                console.log("in")
            }

        });
    };
    

});
PHPzPHPz2773 天前414

全部回复(2)我来回复

  • 巴扎黑

    巴扎黑2017-04-11 13:26:55

    讲下大致的思路,其中dom结构这么放

    <section>
        <img src=x />
        <canvas></canvas>
    </section>

    然后position布局放canvas覆盖在img上面,然后canvas监听touch和mouseMove事件,根据event的layerX/layerY或者offsetX/offsetY得出当前运动的坐标点,然后清理掉canvas上面的图案,清理方法在下面。
    可以做个回掉的监测,当清理的面积(根据半径和移动的xy坐标可算)大于整个canvas面积的70%(随便举例)就算刮奖完成。
    别看讲的这么简单,写完这部分代码,加上各种边界检查等容错得弄几个小时吧。


    以下是原答案:
    建议做两层,然后重叠,canvas默认是png类型有透明通道的,直接刮开canvas下面就看到了奖品的那个图片。
    canvas里面有cleanRect的方法,清理掉矩形区域很好用,但是圆形区域没有直接的接口,但是利用globalCompositeOperation可以hack一个清理圆形区域的方法出来。
    补充一下关于clean arc 方面的一点东西:
    http://stackoverflow.com/ques...

    function cleanArc(context, x, y, radius){
        context.globalCompositeOperation = 'destination-out'
        context.arc(x, y, radius, 0, Math.PI*2, true);
        context.fill();
    }

    回复
    0
  • 阿神

    阿神2017-04-11 13:26:55

    好多代码,厉害了我的哥

    回复
    0
  • 取消回复