图片垂直水平居中并随鼠标移动的非线性动画

首发布于个人博客

1、背景

为什么有这个需求呢,是因为我玩的游戏有这个特效我感觉很cool,就学着模仿一下,做完感觉收益颇多

2、思路

2.1、垂直水平居中

  Object.assign(el.style, {
    // 水平垂直居中的样式
    position: 'fixed',
    margin: 'auto',
    left: '0px',
    right: '0px',
    top: '0px',
    bottom: '0px'
  })

这里我采用了 assign 方法向对象添加属性,居中理解:采用 position 为 fixed 使元素脱离文档流,left 和 right 为 0 决定了该元素宽度充满浏览器,总宽度(固定) = width(固定) + margin-left + margin-right,达到居中的效果

2.2、非线性移动

// t 为当前时间, b 为开始状态, c 为结束状态, d 为持续时间
function move (t, b, c, d) {
  let store = 0
  if (b !== 0) {
    store = b
    c = c - b
    b = 0
  }
  return -c * (t /= d) * (t - 2) + b + store
}
      el.parentNode.onmousemove = function (e) {
        // 函数节流,1 帧内只触发一次
        if (this.time && Date.now() - this.time < 16) return
        this.time = Date.now()
        // 获取鼠标当前距离浏览器中央的坐标
        let mouseX = e.clientX - document.body.clientWidth / 2
        let mouseY = e.clientY - document.body.clientHeight / 2
        // 为绑定的函数赋值
        binding.value(mouseX, mouseY)
      }
        // 控制移动
  Vue.prototype.$moveCenter = function (x, y, style, {xZoom = 1, yZoom = 1, xOffset = 0, yOffset = 0, time = 0, durning = 1} = {}) {
    // 知识点:函数表达式,箭头函数,解构赋值,动画,函数节流,工具类的添加,图像居中,闭包
    // 数据预处理,因为原本的数据为 0px
    // slice() 方法内加负数会自动加上字符串的长度
    let [cx, cy] = [-x / xZoom + xOffset, y / yZoom + yOffset]
    let [bx, by] = [Number.parseInt(style.left.slice(0, -2)), Number.parseInt(style.bottom.slice(0, -2))]
    // 使用箭头函数
    let fn = () => {
      time++
      // 调用缓动函数
      let xValue = move(time, bx, cx, durning)
      let yValue = move(time, by, cy, durning)
      // 字符串拼接
      console.log(xValue)
      style.bottom = yValue + 'px'
      style.left = xValue + 'px'
      if (time <= durning) {
        requestAnimationFrame(fn)
      }
    }
    fn()
  }

首先理解非线性移动的定义,其实就是加速度,一开始我是打算采用两个定时器的方式的,但后来因为性能太差舍弃了,到网上了解到缓动函数的定义,一个定时器就实现了非线性移动

2.3、优化

2.3.1、节流
        // 函数节流,1 帧内只触发一次
        if (this.time && Date.now() - this.time < 16) return
        this.time = Date.now()
2.3.2、requestAnimationFrame

跟定时器差不多,但是它能实现控制屏幕刷新率,不仅如此,在屏幕不可用或最小化时会自动停止,达到 cpu 节能的目的

3、完整代码

let centerMove = {}

// t 为当前时间, b 为开始状态, c 为结束状态, d 为持续时间
function move (t, b, c, d) {
  let store = 0
  if (b !== 0) {
    store = b
    c = c - b
    b = 0
  }
  return -c * (t /= d) * (t - 2) + b + store
}

// Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器。
centerMove.install = function (Vue) {
  // 自定义指令 v-center
  Vue.directive('center', {
    // 使用时要保证父元素所占的空间满足自身要求
    inserted: function (el, binding) {
      // 注册事件
      // 如果有多个元素同时注册事件,下列事件也只会注册多次,执行一次
      // 原因:函数存放位置为堆,传递时用的是指针,因此重复赋值时后者代替前者
      el.parentNode.onmousemove = function (e) {
        // 函数节流,1 帧内只触发一次
        if (this.time && Date.now() - this.time < 16) return
        this.time = Date.now()
        // 获取鼠标当前距离浏览器中央的坐标
        let mouseX = e.clientX - document.body.clientWidth / 2
        let mouseY = e.clientY - document.body.clientHeight / 2
        // 为绑定的函数赋值
        binding.value(mouseX, mouseY)
      }
      Object.assign(el.style, {
        // 水平垂直居中的样式
        position: 'fixed',
        margin: 'auto',
        left: '0px',
        right: '0px',
        top: '0px',
        bottom: '0px'
      })
    }
  })
  // 控制移动
  Vue.prototype.$moveCenter = function (x, y, style, {xZoom = 1, yZoom = 1, xOffset = 0, yOffset = 0, time = 0, durning = 1} = {}) {
    // 知识点:函数表达式,箭头函数,解构赋值,动画,函数节流,工具类的添加,图像居中,闭包
    // 数据预处理,因为原本的数据为 0px
    // slice() 方法内加负数会自动加上字符串的长度
    let [cx, cy] = [-x / xZoom + xOffset, y / yZoom + yOffset]
    let [bx, by] = [Number.parseInt(style.left.slice(0, -2)), Number.parseInt(style.bottom.slice(0, -2))]
    // 使用箭头函数
    let fn = () => {
      time++
      // 调用缓动函数
      let xValue = move(time, bx, cx, durning)
      let yValue = move(time, by, cy, durning)
      // 字符串拼接
      console.log(xValue)
      style.bottom = yValue + 'px'
      style.left = xValue + 'px'
      if (time <= durning) {
        requestAnimationFrame(fn)
      }
    }
    fn()
  }
}
export default centerMove

4、使用方法

4.1、在 main.js 中

import centerMove from './components/tools/index.js'
// 引入自定义插件
Vue.use(centerMove)

4.2、在项目中

  <div class="blogIndex" >
    <img src="../../assets/back.jpg" style="width: 115%" ref="img" v-center="move">
  </div>
  
  <script>
// export default 只是为了导出,类似 new Vue()
export default {
  name: 'blogIndex',
  data () {
    return {
    }
  },
  methods: {
    // 鼠标移动时触发该方法
    move (x, y) {
      this.$moveCenter(x, y, this.$refs.img.style, {
        xZoom: 12,
        yZoom: 5,
        xOffset: -100,
        durning: 30
      })
    }
  }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值