如何使用vue开发波纹点击特效组件

最近在使用 vue2 做一个新的 material ui 库,波纹点击效果在 material design 中被多次使用到,于是决定把它封装成一个公共的组件,使用时直接调用就好啦。

开发之前的思考

常见的波纹点击效果的实现方式是监听元素的 mousedown 事件,在元素内部创建一个 波纹元素 ,并调整元素的 transform:
scale(0); 到 transform: scale(1);, 通过计算点击的位置来设置 波纹元素 的大小和位置,以达到波纹扩散的效果。

我将组件分为两个部分, circleRipple.vue 和 TouchRipple.vue 各自实现不同的功能

  1. circleRipple.vue 波纹扩散组件,完成波纹扩散的效果
  2. TouchRipple.vue 监听 mouse 和 touch 相关事件,控制 circleRipple 的显示,位置。

circleRipple.vue

circleRipple 需要完成波纹扩展的效果,而且可以从外部控制它的大小和位置, 所以利用 vue 的 transition 动画完成效果, 提供mergeStyle 、 color 、opacity 参数来从外部控制它的样式。实现代码如下。


  1. <template> 
  2.   <transition name="mu-ripple"> 
  3.     <div class="mu-circle-ripple" :style="styles"></div> 
  4.   </transition> 
  5. </template> 
  6.  
  7. <script> 
  8. import {merge} from '../utils' 
  9. export default { 
  10.   props: { 
  11.     mergeStyle: { 
  12.       type: Object, 
  13.       default () { 
  14.         return {} 
  15.       } 
  16.     }, 
  17.     color: { 
  18.       type: String, 
  19.       default: '' 
  20.     }, 
  21.     opacity: { 
  22.       type: Number 
  23.     } 
  24.   }, 
  25.   computed: { 
  26.     styles () { 
  27.       return merge({}, {color: this.color, opacity: this.opacity}, this.mergeStyle) 
  28.     } 
  29.   } 
  30. </script> 
  31.  
  32. <style lang="less"> 
  33. @import "../styles/import.less"; 
  34. .mu-circle-ripple{ 
  35.   position: absolute; 
  36.   width: 100%; 
  37.   height: 100%; 
  38.   left: 0; 
  39.   top: 0; 
  40.   pointer-events: none; 
  41.   user-select: none; 
  42.   border-radius: 50%; 
  43.   background-color: currentColor; 
  44.   background-clip: padding-box; 
  45.   opacity: 0.1; 
  46.  
  47. .mu-ripple-enter-active, .mu-ripple-leave-active{ 
  48.   transition: transform 1s @easeOutFunction, opacity 2s @easeOutFunction; 
  49.  
  50. .mu-ripple-enter { 
  51.   transform: scale(0); 
  52.  
  53. .mu-ripple-leave-active{ 
  54.   opacity: 0 !important; 
  55. </style> 

vue2 对于动画方面做了比较大的修改,除了把指令换成组件外,它还可以完成更复杂的动画效果,具体可以看这里 vue2 transition

TouchRipple.vue

TouchRipple 需要控制 circleRipple 的显示。完成以下内容:

  1. 监听 mouse 和 touch 相关事件, 控制 circleRipple 的显示。
  2. 通过点击事件 event 对象, 计算出 circleRipple 的大小和位置
  3. 如果频繁点击可能出现多个 circleRipple

首先,基本模板 + 数据模型


  1. <template> 
  2.   <!--最外层用div包裹--> 
  3.   <div @mousedown="handleMouseDown" @mouseup="end()" @mouseleave="end()" @touchstart="handleTouchStart"  @touchend="end()" @touchcancel="end()"> 
  4.     <!--外层包裹防止波纹溢出--> 
  5.     <div :style="style" ref="holder"> 
  6.       <!--多个波纹用 v-for 控制--> 
  7.       <circle-ripple :key="ripple.key" :color="ripple.color" :opacity="ripple.opacity" :merge-style="ripple.style" v-for="ripple in ripples"></circle-ripple> 
  8.     </div> 
  9.     <!--利用slot分发实际内容--> 
  10.     <slot></slot> 
  11.   </div> 
  12. </template> 
  13.  
  14. <script> 
  15. import circleRipple from './circleRipple' 
  16. export default { 
  17.   props: { 
  18.     // 是否从中间扩散,设为false会从点击处扩散 
  19.     centerRipple: { 
  20.       type: Boolean, 
  21.       default: true 
  22.     }, 
  23.     // 外层包裹的样式 
  24.     style: { 
  25.       type: Object, 
  26.       default () { 
  27.         return { 
  28.           height: '100%', 
  29.           width: '100%', 
  30.           position: 'absolute', 
  31.           top: '0', 
  32.           left: '0', 
  33.           overflow: 'hidden' 
  34.         } 
  35.       } 
  36.     }, 
  37.     // 波纹颜色 
  38.     color: { 
  39.       type: String, 
  40.       default: '' 
  41.     }, 
  42.     // 波纹透明度 
  43.     opacity: { 
  44.       type: Number 
  45.     } 
  46.   }, 
  47.   data () { 
  48.     return { 
  49.       nextKey: 0, // 记录下一个波纹元素的key值, 相当于uuid,不设置的话会使动画失效 
  50.       ripples: [] // 波纹元素参数数组 
  51.     } 
  52.   }, 
  53.   mounted () { 
  54.     this.ignoreNextMouseDown = false // 防止既有 touch 又有 mouse点击的情况 
  55.   }, 
  56.   methods: { 
  57.     start (event, isRippleTouchGenerated) { 
  58.       // 开始波纹效果 
  59.     }, 
  60.     end () { 
  61.       // 结束波纹效果 
  62.     }, 
  63.     handleMouseDown (event) { 
  64.       // 监听 鼠标单击 
  65.     }, 
  66.     handleTouchStart (event) { 
  67.       // 监听 touchstart 方法 
  68.     } 
  69.   }, 
  70.   components: { 
  71.     'circle-ripple': circleRipple 
  72.   } 
  73. </script> 

开始和结束波纹效果

增加一个波纹元素只需要在 ripple 增加一个 object 即可,不同的是当需要从点击处扩展时,需要计算一下波纹元素的大小和位置。


  1.   // isRippleTouchGenerated 是否是touch 事件开始的 
  2.   start (event, isRippleTouchGenerated) { 
  3.     // 过滤 touchstart 和 mousedown 同时存在的情况 
  4.     if (this.ignoreNextMouseDown && !isRippleTouchGenerated) { 
  5.       this.ignoreNextMouseDown = false 
  6.       return 
  7.     } 
  8.      
  9.     // 添加一个 波纹元素组件 
  10.     this.ripples.push({ 
  11.       key: this.nextKey++,  
  12.       color: this.color, 
  13.       opacity: this.opacity, 
  14.       style: this.centerRipple ? {} : this.getRippleStyle(event) // 不是从中心扩展的需要计算波纹元素的位置 
  15.     }) 
  16.     this.ignoreNextMouseDown = isRippleTouchGenerated 
  17.  }, 
  18.  end () { 
  19.    if (this.ripples.length === 0) return 
  20.    this.ripples.splice(0, 1) // 删除一个波纹元素 
  21.    this.stopListeningForScrollAbort() // 结束 touch 滚动的处理 
  22.   } 

因为 vue2 基于 Virtual DOM 的, 所以如果没有 key 在增加一个元素又同时删除一个元素的时候,dom tree并没有发生变化,是不会产生动画效果的。

监听 mousedown 和 touchstart

mousedown 和 touchstart 处理上会有所不同,但都是用来启动波纹效果的, touch涉及到多点点击的问题,我们一般取第一个即可。


  1.     handleMouseDown (event) { 
  2.       // 只监听鼠标左键的点击 
  3.       if (event.button === 0) { 
  4.         this.start(event, false) 
  5.       } 
  6.     }, 
  7.     handleTouchStart (event) { 
  8.       event.stopPropagation() // 防止多个波纹点击组件嵌套 
  9.       if (event.touches) { 
  10.         this.startListeningForScrollAbort(event) // 启动 touchmove 触发滚动处理 
  11.         this.startTime = Date.now() 
  12.       } 
  13.       this.start(event.touches[0], true) 
  14.     } 

touchmove控制

当发生touchMove事件是需要判断是否,移动的距离和时间,然后结束小波纹点击小姑


  1.   // touchmove 结束波纹控制 
  2.   stopListeningForScrollAbort () { 
  3.     if (!this.handleMove) this.handleMove = this.handleTouchMove.bind(this) 
  4.     document.body.removeEventListener('touchmove', this.handleMove, false) 
  5.   }, 
  6.   startListeningForScrollAbort (event) { 
  7.     this.firstTouchY = event.touches[0].clientY 
  8.     this.firstTouchX = event.touches[0].clientX 
  9.     document.body.addEventListener('touchmove', this.handleMove, false) 
  10.   }, 
  11.   handleTouchMove (event) { 
  12.     const timeSinceStart = Math.abs(Date.now() - this.startTime) 
  13.     if (timeSinceStart > 300) { 
  14.       this.stopListeningForScrollAbort() 
  15.       return 
  16.     } 
  17.     const deltaY = Math.abs(event.touches[0].clientY - this.firstTouchY) 
  18.     const deltaX = Math.abs(event.touches[0].clientX - this.firstTouchX) 
  19.     // 滑动范围在 > 6px 结束波纹点击效果 
  20.     if (deltaY > 6 || deltaX > 6) this.end() 
  21.   } 

计算波纹的位置和大小

需要从点击处扩散的波纹效果,需要计算波纹元素的大小和位置


  1.   getRippleStyle (event) { 
  2.     let holder = this.$refs.holder 
  3.     //  这个方法返回一个矩形对象,包含四个属性:left、top、right和bottom。分别表示元素各边与页面上边和左边的距离。 
  4.     let rect = holder.getBoundingClientRect()  
  5.     // 获取点击点的位置 
  6.     let x = event.offsetX 
  7.     let y 
  8.     if (x !== undefined) { 
  9.       y = event.offsetY 
  10.     } else { 
  11.       x = event.clientX - rect.left 
  12.       y = event.clientY - rect.top 
  13.     } 
  14.     // 获取最大边长 
  15.     let max 
  16.     if (rect.width === rect.height) { 
  17.       max = rect.width * 1.412 
  18.     } else { 
  19.       max = Math.sqrt( 
  20.         (rect.width * rect.width) + (rect.height * rect.height) 
  21.       ) 
  22.     } 
  23.     const dim = (max * 2) + 'px' 
  24.     return { 
  25.       width: dim, 
  26.       height: dim, 
  27.       // 通过margin控制波纹中心点和点击点一致 
  28.       'margin-left': -max + x + 'px', 
  29.       'margin-top': -max + y + 'px' 
  30.     } 
  31.   } 

使用

由于 touchRipple 内部都是 position:absolute 布局,使用时,需要在外部加上 position:relative


  1. // listItem.vue 
  2. <a :href="href" @mouseenter="hover = true" @mouseleave="hover = false" @touchend="hover = false" 
  3.     @touchcancel="hover = false" class="mu-item-wrapper" :class="{'hover': hover}"> 
  4.     <touch-ripple class="mu-item" :class="{'mu-item-link': link}" :center-ripple="false"> 
  5.       <div class="mu-item-media"> 
  6.         <slot name="media"></slot> 
  7.       </div> 
  8.       <div class="mu-item-content"> 
  9.         // ... 
  10.       </div> 
  11.     </touch-ripple> 
  12. </a> 
  13. <style> 
  14.  
  15. .mu-item-wrapper { 
  16.     display: block; 
  17.     color: inherit; 
  18.     position: relative; 
  19. </style> 

最后

到这点击波纹组件就开发完了, 这些代码借鉴了 keen-ui 和 material-ui 的实现方式。

作者:绫宇

来源:51CTO

时间: 2024-12-29 09:49:05

如何使用vue开发波纹点击特效组件的相关文章

After Effects制作波纹荡漾文字特效

本教程为After Effects实例教程,其中使用了变形滤镜中的Wave Warp内置滤镜的使用,通过对波动值.透明度的关键帧设置产生动画,同时使用Glow辉光特效,改变波,感受其效果并理解其参数设置.下面请看效果演示: 最终效果 一.运行After Effects软件,执行file--New--New Project(快捷键Ctrl+Alt+N),新建立一个工程文件,如图1所示. 图1 点击Composition--New composition(快捷键Ctrl+N),新建立一个合成项目,如

Android仿知乎客户端关注和取消关注的按钮点击特效实现思路详解_Android

先说明一下,项目代码已上传至github,不想看长篇大论的也可以先去下代码,对照代码,哪里不懂点哪里. 代码在这https://github.com/zgzczzw/ZHFollowButton 前几天发现知乎关注的点击效果确实赞,查了一下实现方式,刚好看到这个问题,花了一天时间终于把这个效果实现了,现在来回答一下,很不幸,楼上各位的答案都不全对,且听我一一道来. 首先,我先详细观察了一些知乎的效果,其中有一个很神奇的地方,如图: 注意看第二张图,这个圆形在扩散的时候,圆形底下的字还在,而且新的

编写带有点击特效的UIButton

编写带有点击特效的UIButton 效果: 源码: // // ViewController.m // Button // // Created by XianMingYou on 15/1/18. // Copyright (c) 2015年 XianMingYou. All rights reserved. // #import "ViewController.h" @interface ViewController () @property (nonatomic, strong)

安卓应用开发-安卓点击下拉按钮弹出编辑界面布局,这种布局叫什么名字

问题描述 安卓点击下拉按钮弹出编辑界面布局,这种布局叫什么名字 不是spinner那种.开发-安卓点击下拉按钮弹出编辑界面布局,这种布局叫什么名字-点击按钮弹出下拉菜单"> 解决方案 http://www.javaapk.com/source/6170.html

android-安卓开发中,点击卸载按钮,弹出一个提示框

问题描述 安卓开发中,点击卸载按钮,弹出一个提示框 解决方案 http://www.cnblogs.com/zealotrouge/p/3159772.html 解决方案二: 给按钮注册onCLick()事件,在里面实现弹出一个dialog就可以了很简单,很多书上都有参考程序 解决方案三: 你要的是这种效果吧: //packageName是要卸载的包名,比如百度贴吧是com.baidu.tieba Uri uri = Uri.parse("package:" + packageName

Android实现水波纹点击效果

Android实现水波纹点击效果只在Android5.0以上版本有效,水波纹点击效果代码供大家参考,具体内容如下 圆角背景的水波纹效果(如上图) 1. 定义一个普通圆角背景的xml; rounded_corners.xml <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"

IOS开发:Unity3D角色控制器组件研究

  使用它的方法如下,首先打开Unity游戏引擎编辑器,然后在Project视图中右键选择Import Package -> Charactr Controller(角色控制器)把它导入我们的工程中.如下图所示,第一人称与第三人称的组建已经加入Project视图中.3rd Person Controller 表示第三人称控制器,First Person Controller表示第一人称控制器. 开发:Unity3D角色控制器组件研究-"> 如下图所示,我们将FirstPerson C

WebFields适用于asp开发环境的自定义字段组件

web WebFields适用于asp开发环境的自定义字段组件,该组件您可以到网站http://www.haitiansoft.com下载组件,并有组件的详细使用说明. WebFields自定义字段组件组件能够让客户或者开发人员进行数据库表字段的动态添加.删除 等操作,由客户自己将系统客户化,让开发出的应用系统更能够满足客户的需要. 组件提供相应的记录编辑.新建.查询.显示等一系列的方法,对于应用系统的产品 化有重要作用. WebFields自定义字段组件组件含有多种数据类型,字符型.数据型.下

android浏览器开发,地址栏用什么组件怎样设置成当获得焦点时输入法中有“前往”或“搜索”键

问题描述 android浏览器开发,地址栏用什么组件怎样设置成当获得焦点时输入法中有"前往"或"搜索"键 想用android写一个android浏览器的小DEMO,地址栏用的原生EDITTEXT组件,说到这里那么问题来了..在EDITTEXT中输入完毕之后,弹出的输入法中没有"前往"或者"搜索"键,网上下载的浏览器比如UC浏览器,地址栏输入后键盘上都有有一个"前往"按钮.这种情况应该如何设置? 解决方案 我