情人节H5页面,一个程序猿的小小心意

初衷

情人节快到了,本人一直在外地,没能回家,和女友也无法dating,于是想给她一点特别的小惊喜。不知怎么想的,写个H5吧,女友在这方面见识得不多,应该会喜欢吧。

调了几个元素:雪花(最后并没有具象成雪花状,用了白色半透明圆点来替代)、文字粒子化。

这个创意很早以前就有了,13年HTML5刚风行的时候,就有google的大神写了一个粒子化的神作,后来也不断有人模仿。

原理

利用canvas 的 imageData 这个对象,我们可以取到当前canvas 里面具有rgba信息的点。有着这个,我们就可以操控已有的粒子去填充那些点,这样我们不但可以粒子化文字,图片、符号等等,只要是能画上canvas 的,都可以用这种方法粒子化。

实现思路

首先我们初始化一堆粒子,并让它们在canvas里面以不同的速度不同的角度运动。 变量定义:

data: function () {
    return {
      context: "HAPPY Valentine's DAY", // 文字内容
      can: null, // canvas
      cxt: null, // canvas context
      width: window.innerWidth, // 屏幕宽度
      height: window.innerHeight, // 屏幕高度
      particles: [],  //在背景中运动的粒子
      particlesToBeMoved: [],  //被选中去填充文字的粒子
      points: [], //imageData中选出来的电
      particleNum: 1500, //可用的粒子数,可以自定义
      intervalId: 0, // setinterval id,方便取消
      power: 1  //  倍率
    }
  },
复制代码

功能函数:

1、initBackground 是用来初始化particles 给每个粒子都随机化赋予了坐标、弧度、透明度等等,这里的moveDirec 是它之后运动的方向 2、draw 函数是用来绘制particles中的粒子 3、moveRestParticles实现按照moveDirect进行移动的功能。我们还做了判断,如果遇到墙壁,则反弹。

    initBackground: function () {
      this.can = document.getElementById('canvas')
      this.cxt = this .can.getContext('2d')
      for (let i = 0; i < this.particleNum; i++) {
        this.particles.push({
          position: {x: this.getRandom(this.width), y: this.getRandom(this.height)},
          moveDirec: {x: Math.random() > 0.5 ? Math.random() * 5 : -Math.random() * 5, y: Math.random() > 0.5 ? Math.random() * 5 : -Math.random() * 5},
          size: this.getRandom(1) * 0.5,
          opacity: this.getRandom(1)
        })
      }
    },
    draw: function () {
      this.cxt.clearRect(0, 0, this.can.width, this.can.height)
      this.particles.concat(this.particlesToBeMoved).forEach(p => {
        this.cxt.fillStyle = 'rgba(255, 255, 255, ' + p.opacity + ')'
        this.cxt.beginPath()
        this.cxt.arc(p.position.x, p.position.y, p.size * 10, 0, 2 * Math.PI, true)
        this.cxt.fill()
      })
    },
    moveRestParticles: function () {
      this.particles.forEach(p => {
        p.position.x += p.moveDirec.x
        p.position.y += p.moveDirec.y
        if (p.position.x > this.width || p.position.x <= 0) {
          p.moveDirec.x = -p.moveDirec.x
        }
        if (p.position.y > this.height || p.position.y <= 0) {
          p.moveDirec.y = -p.moveDirec.y
        }
      })
      this.draw()
    }
复制代码

我们只要用 setInterval(this.moveRestParticles, 10) 就能让粒子动起来了。

第一步就完成了,下面要完成文字像素提取,并且移动粒子到像素点。

makeWords 是将文字显示在canvas 上 getPoint 是获取刚在画在canvas 上的文字的像素信息,也就是开头提到的 imageData 我们把符合条件的点(point) 放在数组里。这里的筛选方法是不完全的,也就是,我们并没有把全部点都筛选出来,而是筛选了一部分,因为如果全部筛选的话,会使得粒子数量庞大,在视觉上显得是很凌乱。如果你想的话,可以吧 x+=4 y+=4 修改成 x+=1 y+=1

    makeWords: function (word, cxt) {
      cxt.font = '80px Adele bold'
      cxt.fillStyle = 'rgba(100, 100, 100, 256)'
      cxt.fillText(word, this.can.width / 10, this.can.height / 4)
      cxt.save()
    },
    getPoint: function (can) {
      let cxt = can.getContext('2d')
      let imgData = cxt.getImageData(0, 0, can.width, can.height)
      let points = []
      for (let x = 0; x < imgData.width; x += 4) {
        for (let y = 0; y < imgData.height; y += 4) {
          let i = (y * imgData.width + x) * 4
          if (imgData.data[i] <= 128 && imgData.data[i + 1] <= 128 && imgData.data[i + 2] <= 128 && imgData.data[i + 3] >= 128) {
            let point = new Point(x - 3, y - 3, cxt)
            points.push(point)
          }
        }
      }
      return points
    }
复制代码

showWords 是入口函数,用了Promise 是方便后面控制多个语句的显示。

makeAnitation 是移动像素点,我们先打乱了粒子,让画面看上去更自然。后面的移动函数就很简单了,一看就能懂。

    showWords: function () {
      return new Promise((resolve, reject) => {
        this.makeWords(this.context, this.cxt)
        this.points = this.getPoint(this.can)
        this.cxt.clearRect(0, 0, this.can.width, this.can.height)
        if (this.points.length > this.particleNum) {
          console.log(this.points.length)
          console.log('超出数量')
          resolve()
        } else {
          this.makeAnimation()
          setTimeout(resolve, 10000)
        }
      })
    },
    makeAnimation: function () {
      this.shuffleParticle()
      this.getParticlesToBeMoved()
      this.judgeTheDirect()
      this.intervalId = setInterval(this.moveParticles, 10)
    },
    clearAnimation: function () {
      clearInterval(this.intervalId)
      this.particles.push(...this.particlesToBeMoved)
      this.particlesToBeMoved = []
      this.intervalId = setInterval(this.moveRestParticles, 10)
    },

    shuffleParticle: function () {
      this.particles = _.shuffle(this.particles)
    },
    getParticlesToBeMoved: function () {
      for (let i = 0; i < this.points.length; i++) {
        this.particlesToBeMoved.push(this.particles.shift())
      }
    },
    judgeTheDirect: function () {
      for (let i = 0; i < this.points.length; i++) {
        if (this.points[i].x - this.particlesToBeMoved[i].position.x < 0) {
          this.points[i].dx = -this.points[i].dx
        }
        if (this.points[i].y - this.particlesToBeMoved[i].position.y < 0) {
          this.points[i].dy = -this.points[i].dy
        }
        if (this.points[i].size - this.particlesToBeMoved[i].size < 0) {
          this.points[i].ds = -this.points[i].ds
        }
        if (this.points[i].opacity - this.particlesToBeMoved[i].opacity < 0) {
          this.points[i].do = -this.points[i].do
        }
      }
    },
    moveParticles: function () {
      for (let i = 0; i < this.points.length; i++) {
        if (Math.abs(this.points[i].x - this.particlesToBeMoved[i].position.x) <= Math.abs(this.points[i].dx) || this.particlesToBeMoved[i].position.x < 0) {
          this.particlesToBeMoved[i].position.x = this.points[i].x
        } else {
          this.particlesToBeMoved[i].position.x += this.points[i].dx
        }

        if (Math.abs(this.points[i].y - this.particlesToBeMoved[i].position.y) <= Math.abs(this.points[i].dy) || this.particlesToBeMoved[i].position.y < 0) {
          this.particlesToBeMoved[i].position.y = this.points[i].y
        } else {
          this.particlesToBeMoved[i].position.y += this.points[i].dy
        }
        if (Math.abs(this.points[i].size - this.particlesToBeMoved[i].size) <= Math.abs(this.points[i].ds) || this.particlesToBeMoved[i].size < 0) {
          this.particlesToBeMoved[i].size = this.points[i].size
        } else {
          this.particlesToBeMoved[i].size += this.points[i].ds
        }

        if (Math.abs(this.points[i].opacity - this.particlesToBeMoved[i].opacity) <= Math.abs(this.points.do) || this.particlesToBeMoved[i].opacity < 0) {
          this.particlesToBeMoved[i].opacity = this.points[i].opacity
        } else {
          this.particlesToBeMoved[i].opacity += this.points[i].do
        }
      }
      this.moveRestParticles()
      //  this.draw()
    },
复制代码

最后放一下效果图:

完整代码详见github

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值