vue实现水波涟漪效果的点击反馈指令


Posted in Vue.js onMay 31, 2021

水波效果

当用户点击时,会以点击中心为圆心产生一个水波扩散的涟漪效果,适用各个场景,美观又不浮夸,关键是可以给用户带来很直观的反馈。

vue实现水波涟漪效果的点击反馈指令

来看实现

首先这里基于Vue3自定义指令进行封装,Vue3的自定义指令跟Vue2相比变动不是很大,。我们的目标是完成一个水波指令的基本原型,这里循序渐进展开。

定制一个水波纹默认样式

水波纹实际上就是通过用户点击的位置生成一个小圆圈,并且尺寸逐渐扩大到整个被点击元素的一个过程,所以这里先制定一个水波基本的样式,并设置好过度动画,过度动画应该是一个先慢后快的一个过程,这里使用贝塞尔曲线定制

.my-ripple {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  border-radius: 50%;
  background-color: currentColor;
  opacity: 0;
  transition: transform 0.2s cubic-bezier(0.68, 0.01, 0.62, 0.6), opacity 0.08s linear;
  will-change: transform, opacity;
  pointer-events: none;
}

计算水波纹的位置和直径

如果确定了水波的直径、创建时的(x,y)、过度动画结束时的(x,y),我们就可以通过transition去渲染水波动画了,创建时的(x,y)就是用户点击的位置,但是水波的直径和过度动画结束时的(x,y)怎么计算呢?我们的元素都是矩形,不论用户从元素的任意坐标进行点击,以矩形斜边作为直径的圆都可以完美的覆盖整个元素,斜边的计算我们利用小学数学知识求两边平方和进行开方得到,下面是过度动画结束时的水波推演图。

第一个箭头: 期望得到的水波
第二个箭头: 元素(0,0)点创建的水波
第三个箭头: 元素(0,0)点创建的水波, 不带圆角效果

vue实现水波涟漪效果的点击反馈指令

我们可以发现通过元素(0,0)点创建的水波进行一定偏移就可以得到我们想要的水波,由此我们可以推断出

动画结束时的水波的尺寸 = 圆的斜边
创建时的(x,y) = 用户点击的位置
过度动画结束时的(x,y) = 元素(0,0)点创建的水波进行x和y的偏移得到

function computeRippleStyles(element, event) {
  const { top, left } = element.getBoundingClientRect()
  const { clientWidth, clientHeight } = element
  
  const radius = Math.sqrt(clientWidth ** 2 + clientHeight ** 2) / 2
  const size = radius * 2
  
  const localX = event.clientX - left
  const localY = event.clientY - top

  const centerX = (clientWidth - radius * 2) / 2
  const centerY = (clientHeight - radius * 2) / 2

  const x = localX - radius
  const y = localY - radius

  return { x, y, centerX, centerY, size }
}

鼠标按下时创建水波

然后我们需要在鼠标按下时创建水波,监听鼠标按下的事件,这里以pc端为例子,刚创建水波时使用transform缩小到0.3,这是作者尝试过相对合适的创建大小, 然后修改transform触发过度水波扩散动画,这里还加入了透明度的过度,可以使水波涟漪更有质感。

function createRipple(event) {
  const container = this
  const { x, y, centerX, centerY, size } = computeRippleStyles(container, event)
  const ripple = document.createElement('div')
  ripple.classList.add('my-ripple')
  ripple.style.opacity = `0`
  ripple.style.transform = `translate(${x}px, ${y}px) scale3d(.3, .3, .3)`
  ripple.style.width = `${size}px`
  ripple.style.height = `${size}px`
  // 记录水波的创建时间
  ripple.dataset.createdAt = String(performance.now())

  const { position } = window.getComputedStyle(container)
  container.style.overflow = 'hidden'
  position === 'static' && (this.style.position = 'relative')

  container.appendChild(ripple)

  window.setTimeout(() => {
    ripple.style.transform = `translate(${centerX}px, ${centerY}px) scale3d(1, 1, 1)`
    ripple.style.opacity = `.25`
  })
}

const VRipple = {
  mounted(el) {
    el.addEventListener('mousedown', createRipple)
  }
}

vue实现水波涟漪效果的点击反馈指令

鼠标抬起时销毁水波

当鼠标抬起时,只需要找到这个生成的水波节点修改透明度,再等到透明度修改动画结束之后将水波纹节点移除即可

function removeRipple() {
  const container = this
  const ripples = container.querySelectorAll('.my-ripple')
  if (!ripples.length) {
    return
  }

  const lastRipple = ripples[ripples.length - 1]
  // 通过水波的创建时间计算出扩散动画还需要执行多久,确保每一个水波都完整的执行了扩散动画
  const delay = 300 - performance.now() + Number(lastRipple.dataset.createdAt)

  setTimeout(() => {
    lastRipple.style.opacity = `0`
    
    setTimeout(() => lastRipple.parentNode?.removeChild(lastRipple), 300)
  }, delay)
}

const VRipple = {
  mounted(el) {
    el.addEventListener('mousedown', createRipple)
    document.addEventListener('mouseup', removeRipple)
  },
  unmounted(el) {
    el.removeEventListener('mousedown', createRipple)
    document.removeEventListener('mouseup', removeRipple)
  }
}

通过指令binding去扩展你的水波选项

你还可以通过binding去扩展你的指令,比如可以提供修改颜色,禁用状态等等选项,这里就不详细展开了。我们来看一下成果。

vue实现水波涟漪效果的点击反馈指令

写在最后

到此为止我们就实现了一个简单的ripple指令,在我们的组件库中也有这样的指令,所以更完善的版本可以去看我们的源码。 先要感谢一下掘金社区,已经有一部分小伙伴开始pr一些代码到我们的仓库中来,我们也很高兴能和社区的小伙伴们去一起做这样一件事情,另外我们的组件库团队一直在募集爱好者来参与贡献,有兴趣的小伙伴欢迎加入讨论,加入方式就是直接去仓库提issue留邮箱,我们会第一时间处理,有没有兴趣都希望为我们点点star,关注一下我们,社区小伙伴的支持和兴趣是我们最大的动力。

仓库地址
文档地址

以上就是vue实现水波涟漪效果的点击反馈指令的详细内容,更多关于vue 点击反馈指令的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue 插槽简介及使用示例
Nov 19 Vue.js
解决vue下载后台传过来的乱码流的问题
Dec 05 Vue.js
vue实现滚动鼠标滚轮切换页面
Dec 13 Vue.js
解决vue使用vant轮播组件swipe + flex时文字抖动问题
Jan 07 Vue.js
详解实现vue的数据响应式原理
Jan 20 Vue.js
Vue 集成 PDF.js 实现 PDF 预览和添加水印的步骤
Jan 22 Vue.js
Vue单页面应用中实现Markdown渲染
Feb 14 Vue.js
关于Vue Router的10条高级技巧总结
May 06 Vue.js
vue项目多环境配置(.env)的实现
Jul 21 Vue.js
Vue鼠标滚轮滚动切换路由效果的实现方法
Aug 04 Vue.js
详解Vue中$props、$attrs和$listeners的使用方法
Feb 18 Vue.js
vue route新窗口跳转页面并且携带与接收参数
Apr 10 Vue.js
vue 实现上传组件
May 31 #Vue.js
vue基于Teleport实现Modal组件
Vue+Element UI实现概要小弹窗的全过程
vue-cli4.5.x快速搭建项目
Vue CLI中模式与环境变量的深入详解
springboot+VUE实现登录注册
May 27 #Vue.js
vue+springboot实现登录验证码
You might like
PHP实现把数字ID转字母ID
2013/08/12 PHP
typecho插件编写教程(三):保存配置
2015/05/28 PHP
Prototype使用指南之base.js
2007/01/10 Javascript
设置下载不需要倒计时cookie(倒计时代码)
2008/11/19 Javascript
javascript instanceof 与typeof使用说明
2010/01/11 Javascript
js 判断计算字符串长度/判断空的简单方法
2013/08/05 Javascript
jquery JSON的解析方式示例介绍
2014/07/27 Javascript
jQuery实现自动滚动到页面顶端的方法
2015/05/22 Javascript
js实现温度计时间样式代码分享
2015/08/21 Javascript
JavaScript类型系统之正则表达式
2016/01/05 Javascript
Google 地图类型详解及示例代码
2016/08/06 Javascript
nodejs个人博客开发第五步 分配数据
2017/04/12 NodeJs
JavaScript实现设置默认日期范围为最近40天的方法分析
2017/07/12 Javascript
集成vue到jquery/bootstrap项目的方法
2018/02/10 jQuery
JS div匀速移动动画与变速移动动画代码实例
2019/03/26 Javascript
使用vuex较为优雅的实现一个购物车功能的示例代码
2019/12/09 Javascript
js实现鼠标滑动到某个div禁止滚动
2020/09/17 Javascript
解决antd datepicker 获取时间默认少8个小时的问题
2020/10/29 Javascript
编写同时兼容Python2.x与Python3.x版本的代码的几个示例
2015/03/30 Python
Python中的深拷贝和浅拷贝详解
2015/06/03 Python
Python实现将16进制字符串转化为ascii字符的方法分析
2017/07/21 Python
Python操作MongoDB数据库的方法示例
2018/01/04 Python
10个Python小技巧你值得拥有
2018/09/29 Python
python文件拆分与重组实例
2018/12/10 Python
在python3中pyqt5和mayavi不兼容问题的解决方法
2019/01/08 Python
Python学习笔记之读取文件、OS模块、异常处理、with as语法示例
2019/06/04 Python
pyqt5 lineEdit设置密码隐藏,删除lineEdit已输入的内容等属性方法
2019/06/24 Python
在python中用url_for构造URL的方法
2019/07/25 Python
详解HTML5常用的语义化标签
2019/09/27 HTML / CSS
在网络中有两台主机A和B,并通过路由器和其他交换设备连接起来,已经确认物理连接正确无误,怎么来测试这两台机器是否连通?如果不通,怎么来判断故障点?怎么排
2014/01/13 面试题
保送生自荐信范文
2013/10/06 职场文书
八年级物理教学反思
2014/01/19 职场文书
公务员学习习总书记“三严三实”思想汇报
2014/09/19 职场文书
新教师教学工作总结
2015/08/12 职场文书
openstack中的rpc远程调用的方法
2021/07/09 Python
i7 6700处理器相当于i5几代
2022/04/19 数码科技