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 elementUI 使用el-select 时 change事件的触发问题
Nov 17 Vue.js
在Vue中使用mockjs代码实例
Nov 25 Vue.js
详解vue中使用transition和animation的实例代码
Dec 12 Vue.js
Vue如何跨组件传递Slot的实现
Dec 14 Vue.js
基于vue+echarts数据可视化大屏展示的实现
Dec 25 Vue.js
SpringBoot+Vue 前后端合并部署的配置方法
Dec 30 Vue.js
vue二选一tab栏切换新做法实现
Jan 19 Vue.js
vue 中 get / delete 传递数组参数方法
Mar 23 Vue.js
vue首次渲染全过程
Apr 21 Vue.js
Vue中插槽slot的使用方法与应用场景详析
Jun 08 Vue.js
vue实现拖拽交换位置
Apr 07 Vue.js
vue封装数字翻牌器
Apr 20 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自动获取字符串编码函数mb_detect_encoding
2011/05/31 PHP
destoon二次开发入门示例
2014/06/20 PHP
PHP封装CURL扩展类实例
2015/07/28 PHP
php+Ajax无刷新验证用户名操作实例详解
2019/03/04 PHP
php框架知识点的整理和补充
2021/03/01 PHP
防止动态加载JavaScript引起的内存泄漏问题
2009/10/08 Javascript
IE无法设置短域名下Cookie
2010/09/23 Javascript
Jquery下:nth-child(an+b)的使用注意
2011/05/28 Javascript
jQuery控制输入框只能输入数值的小例子
2013/03/20 Javascript
ScrollDown的基本操作示例
2013/06/09 Javascript
js中的this关键字详解
2013/09/25 Javascript
javascript阻止浏览器后退事件防止误操作清空表单
2013/11/22 Javascript
js如何设置在iframe框架中指定div不显示
2013/12/04 Javascript
JQuery打造省市下拉框联动效果
2014/05/18 Javascript
DOM 事件流详解
2015/01/20 Javascript
jquery实现仿新浪微博带动画效果弹出层代码(可关闭、可拖动)
2015/10/12 Javascript
jQuery实现鼠标经过事件的延时处理效果
2020/08/20 Javascript
jquery插件jquery.dragscale.js实现拖拽改变元素大小的方法(附demo源码下载)
2016/02/25 Javascript
js获取元素下的第一级子元素的方法(推荐)
2017/03/05 Javascript
移动端利用H5实现压缩图片上传功能
2017/03/29 Javascript
jQuery 实现双击编辑表格功能
2017/06/19 jQuery
Node解决简单重复问题系列之Excel内容的获取
2018/01/02 Javascript
微信小程序如何调用新闻接口实现列表循环
2019/07/02 Javascript
JS数组方法reduce的用法实例分析
2020/03/03 Javascript
vue项目中使用bpmn-自定义platter的示例代码
2020/05/11 Javascript
[02:53]DOTA2英雄基础教程 山岭巨人小小
2013/12/09 DOTA
[01:04:06]DOTA2上海特级锦标赛A组资格赛#2 Secret VS EHOME第一局
2016/02/26 DOTA
再谈Python中的字符串与字符编码(推荐)
2016/12/14 Python
python 计算数据偏差和峰度的方法
2019/06/29 Python
python 用Matplotlib作图中有多个Y轴
2020/11/28 Python
canvas进阶之如何画出平滑的曲线
2018/10/15 HTML / CSS
New Balance英国官方网站:始于1906年,百年慢跑品牌
2016/12/07 全球购物
大学生自我评价范文分享
2014/02/21 职场文书
学生个人自我鉴定范文
2014/03/28 职场文书
专项资金申请报告
2015/05/15 职场文书
小学生节约用水倡议书
2019/08/12 职场文书