canvas实现刮刮乐,带节流效果,兼容移动端

CSS布局的思路,是让图片绝对定位,并设置z-index=-1,使得它能被canvas挡住。然后canvas的颜色设为透明,从而让图片显示。

JS部分。对节流函数进行了一些修改。thisArg指定fn的this。节流主要是用在mousemove和touchstart,产生涂抹效果那里。

function throttle(fn, interval, thisArg) {
  let waiting = false
  return function (...args) {
    if (waiting) return
    waiting = true
    setTimeout(() => {
      fn.apply(thisArg, args)
      waiting = false
    }, interval)
  }
}

init函数,因为loadImg是异步方法,索性也给他加上了Promise。

事件绑定的部分,我查到的资料似乎都是在mousedown事件的listener里嵌套mousemove的listener,感觉似乎并没有更好的方案了。

mousemove和touchstart各写一个listener,这是为了兼容移动端。

  1. mousemove可以直接用e.offsetX获得鼠标相对#canvas的坐标。
  2. 但touchstart只能event.touches[0].clientX获得触屏坐标相对于浏览器窗口左上角的坐标,因此还需要减去offsetLeft、offsetTop,来获得相对#canvas的坐标。

用到canvas的一些api如下

  • this.ctx.beginPath(),开始一条路径,或重置当前的路径
  • this.ctx.fillStyle = ‘grey’,用来填充颜色,或者提供渐变
  • this.ctx.fillRect(0, 0, this.w, this.h)
  • this.ctx.arc(x, y, 30, 0, Math.PI * 2),用来画扇形的,半径是30px,起始和结束弧度分别是0和2*pi。
  • this.ctx.fill(),紧跟fillStyle
  • this.ctx.closePath(),可以用来创建闭合路径
  • this.ctx.getImageData(0, 0, this.w, this.h).data,获得像素数据,然后就可以计算有多少点已经透明了。这实现了涂到一定程度就自行消失的效果。

下面的例子展示了canvas画三角形,并设置从黑到白的渐变色。其实我还没完全学懂,代码先放这了

'use strict';

let canvas = $('#canvas')[0]
let ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(30, 30)
ctx.lineTo(30, 130)
ctx.lineTo(110, 190)
ctx.closePath()
ctx.stroke()
let colorChange = ctx.createLinearGradient(0, 0, 0, 170)
colorChange.addColorStop(0, "black")
colorChange.addColorStop(1, "white")
ctx.fillStyle = colorChange
ctx.fill()

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>刮开有奖</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <link rel="stylesheet" href="index.css">
  </head>
  <body>
    <div class="container">
      <img class="img" src="1.jpg">
      <canvas id="canvas" width="400" height="300"></canvas>
    </div>
  </body>
  <script src="index.js"></script>
</html>

CSS

body{
  margin: 0;
  display: flex;
  justify-content: center;
}

.container{
  margin-top: 100px;
  position: relative;
  user-select: none;
}

.container .img{
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  width: 400px;
  height: 300px;
}

JS

'use strict';

function throttle(fn, interval, thisArg) {
  let waiting = false
  return function (...args) {
    if (waiting) return
    waiting = true
    setTimeout(() => {
      fn.apply(thisArg, args)
      waiting = false
    }, interval)
  }
}

class ScratchCard {
  constructor() {
    this.canvas = $('#canvas')[0]
    this.img = $('.img')[0]
    this.container = $('.container')[0]
    this.ctx = this.canvas.getContext('2d')
    this.w = this.canvas.width
    this.h = this.canvas.height
    this.area = this.w * this.h
  }

  loadImg() {
    return new Promise((resolve) => {
      let paths = ['1.jpg', '2.jpg']
      this.img.onload = resolve
      this.img.src = paths[parseInt(Math.random() * paths.length)]
    })
  }

  init() {
    return new Promise((resolve) => {
      this.ctx.beginPath()
      this.ctx.fillStyle = 'grey'
      this.ctx.fillRect(0, 0, this.w, this.h)
      resolve()
    }).then(this.loadImg.bind(this))
  }

  addEvent() {
    // let device = /android|iphone|ipad|ipod|webos|iemobile|opear mini|linux/i.test(navigator.userAgent.toLowerCase())
    // let startEvtName = device ? 'touchstart' : 'mousedown'
    // let moveEvtName = device ? 'touchmove' : 'mousemove'
    // let endEvtName = device ? 'touchend' : 'mouseup'

    $(this.canvas).bind('mousedown', () => {
      let throttleMove = throttle((e) => {
        let x = e.offsetX, y = e.offsetY
        // console.log(x, y)//
        this.eraseGrey(x, y)
      }, 50, this)
      $(this.canvas).bind('mousemove', (e) => throttleMove(e))
    })

    $(this.canvas).bind('touchstart', () => {
      let throttleMove = throttle((event) => {
        let x = event.touches[0].clientX - this.container.offsetLeft,
          y = event.touches[0].clientY - this.container.offsetTop
        // console.log(x, y)//
        this.eraseGrey(x, y)
      }, 50, this)
      $(this.canvas).bind('touchmove', () => throttleMove(event))
    })

    $(this.canvas).bind('mouseup', () => {
      $(this.canvas).unbind('mousemove')
    })

    $(this.canvas).bind('touchend', () => {
      $(this.canvas).unbind('touchmove')
    })
  }

  eraseGrey(x, y) {
    this.ctx.globalCompositeOperation = 'destination-out'
    this.ctx.beginPath()
    this.ctx.arc(x, y, 30, 0, Math.PI * 2)
    this.ctx.fill()
    this.ctx.closePath()
    if (this.erasedEnough()) {
      this.ctx.clearRect(0, 0, this.w, this.h)
      $(this.canvas).unbind('mousedown')
      $(this.canvas).unbind('mousemove')
      $(this.canvas).unbind('mouseup')
      $(this.canvas).unbind('touchstart')
      $(this.canvas).unbind('touchmove')
      $(this.canvas).unbind('touchend')
    }
  }

  erasedEnough() {
    let data = this.ctx.getImageData(0, 0, this.w, this.h).data
    let s = 0
    for (let i = 3; i < data.length; i += 4) {
      if (!data[i]) ++s
    }
    return s >= 0.8 * this.area
  }
}

let sc = new ScratchCard()
sc.init().then(sc.addEvent.bind(sc))
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用canvas实现撒彩的代码示例: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8 <title>Canvas实现撒彩</title> <style type="text/css"> body { margin: 0; padding: 0; background-color: #000; } canvas { display: block; position: fixed; top: 0; left: 0; z-index: -1; } </style> </head> <body> <canvas id="mycanvas"></canvas> <script type="text/javascript"> var canvas = document.getElementById('mycanvas'); var ctx = canvas.getContext('2d'); canvas.width = document.documentElement.clientWidth; canvas.height = document.documentElement.clientHeight; var particles = []; var particleCount = 100; var particleSpeed = 2; var particleSize = 3; var colors = ['#bf3242', '#ebebeb', '#ffffff', '#d9d9d9']; function Particle() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.vx = ((Math.random() - 0.5) * particleSpeed); this.vy = ((Math.random() - 0.5) * particleSpeed); this.size = Math.random() * particleSize; this.color = colors[Math.floor(Math.random() * colors.length)]; } Particle.prototype.draw = function() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.closePath(); ctx.fillStyle = this.color; ctx.fill(); } function createParticles() { for (var i = 0; i < particleCount; i++) { particles.push(new Particle()); } } function drawParticles() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (var i = 0; i < particles.length; i++) { particles[i].x += particles[i].vx; particles[i].y += particles[i].vy; if (particles[i].x > canvas.width) { particles[i].x = 0; } if (particles[i].x < 0) { particles[i].x = canvas.width; } if (particles[i].y > canvas.height) { particles[i].y = 0; } if (particles[i].y < 0) { particles[i].y = canvas.height; } particles[i].draw(); } requestAnimationFrame(drawParticles); } createParticles(); drawParticles(); </script> </body> </html> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值