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

开发之前的思考

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

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

circleRipple.vue 波纹扩散组件,完成波纹扩散的效果

TouchRipple.vue 监听 mouse 和 touch 相关事件,控制 circleRipple 的显示,位置。

circleRipple.vue

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

import {merge} from '../utils'

export default {

props: {

mergeStyle: {

type: Object,

default () {

return {}

}

},

color: {

type: String,

default: ''

},

opacity: {

type: Number

}

},

computed: {

styles () {

return merge({}, {color: this.color, opacity: this.opacity}, this.mergeStyle)

}

}

}

@import "../styles/import.less";

.mu-circle-ripple{

position: absolute;

width: 100%;

height: 100%;

left: 0;

top: 0;

pointer-events: none;

user-select: none;

border-radius: 50%;

background-color: currentColor;

background-clip: padding-box;

opacity: 0.1;

}

.mu-ripple-enter-active, .mu-ripple-leave-active{

transition: transform 1s @easeOutFunction, opacity 2s @easeOutFunction;

}

.mu-ripple-enter {

transform: scale(0);

}

.mu-ripple-leave-active{

opacity: 0 !important;

}

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

TouchRipple.vue

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

监听 mouse 和 touch 相关事件, 控制 circleRipple 的显示。

通过点击事件 event 对象, 计算出 circleRipple 的大小和位置

如果频繁点击可能出现多个 circleRipple

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

import circleRipple from './circleRipple'

export default {

props: {

// 是否从中间扩散,设为false会从点击处扩散

centerRipple: {

type: Boolean,

default: true

},

// 外层包裹的样式

style: {

type: Object,

default () {

return {

height: '100%',

width: '100%',

position: 'absolute',

top: '0',

left: '0',

overflow: 'hidden'

}

}

},

// 波纹颜色

color: {

type: String,

default: ''

},

// 波纹透明度

opacity: {

type: Number

}

},

data () {

return {

nextKey: 0, // 记录下一个波纹元素的key值, 相当于uuid,不设置的话会使动画失效

ripples: [] // 波纹元素参数数组

}

},

mounted () {

this.ignoreNextMouseDown = false // 防止既有 touch 又有 mouse点击的情况

},

methods: {

start (event, isRippleTouchGenerated) {

// 开始波纹效果

},

end () {

// 结束波纹效果

},

handleMouseDown (event) {

// 监听 鼠标单击

},

handleTouchStart (event) {

// 监听 touchstart 方法

}

},

components: {

'circle-ripple': circleRipple

}

}

开始和结束波纹效果

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

{

// isRippleTouchGenerated 是否是touch 事件开始的

start (event, isRippleTouchGenerated) {

// 过滤 touchstart 和 mousedown 同时存在的情况

if (this.ignoreNextMouseDown && !isRippleTouchGenerated) {

this.ignoreNextMouseDown = false

return

}

// 添加一个 波纹元素组件

this.ripples.push({

key: this.nextKey++,

color: this.color,

opacity: this.opacity,

style: this.centerRipple ? {} : this.getRippleStyle(event) // 不是从中心扩展的需要计算波纹元素的位置

})

this.ignoreNextMouseDown = isRippleTouchGenerated

},

end () {

if (this.ripples.length === 0) return

this.ripples.splice(0, 1) // 删除一个波纹元素

this.stopListeningForScrollAbort() // 结束 touch 滚动的处理

}

}

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

监听 mousedown 和 touchstart

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

{

handleMouseDown (event) {

// 只监听鼠标左键的点击

if (event.button === 0) {

this.start(event, false)

}

},

handleTouchStart (event) {

event.stopPropagation() // 防止多个波纹点击组件嵌套

if (event.touches) {

this.startListeningForScrollAbort(event) // 启动 touchmove 触发滚动处理

this.startTime = Date.now()

}

this.start(event.touches[0], true)

}

}

touchmove控制

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

{

// touchmove 结束波纹控制

stopListeningForScrollAbort () {

if (!this.handleMove) this.handleMove = this.handleTouchMove.bind(this)

document.body.removeEventListener('touchmove', this.handleMove, false)

},

startListeningForScrollAbort (event) {

this.firstTouchY = event.touches[0].clientY

this.firstTouchX = event.touches[0].clientX

document.body.addEventListener('touchmove', this.handleMove, false)

},

handleTouchMove (event) {

const timeSinceStart = Math.abs(Date.now() - this.startTime)

if (timeSinceStart > 300) {

this.stopListeningForScrollAbort()

return

}

const deltaY = Math.abs(event.touches[0].clientY - this.firstTouchY)

const deltaX = Math.abs(event.touches[0].clientX - this.firstTouchX)

// 滑动范围在 > 6px 结束波纹点击效果

if (deltaY > 6 || deltaX > 6) this.end()

}

}

计算波纹的位置和大小

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

{

getRippleStyle (event) {

let holder = this.$refs.holder

// 这个方法返回一个矩形对象,包含四个属性:left、top、right和bottom。分别表示元素各边与页面上边和左边的距离。

let rect = holder.getBoundingClientRect()

// 获取点击点的位置

let x = event.offsetX

let y

if (x !== undefined) {

y = event.offsetY

} else {

x = event.clientX - rect.left

y = event.clientY - rect.top

}

// 获取最大边长

let max

if (rect.width === rect.height) {

max = rect.width * 1.412

} else {

max = Math.sqrt(

(rect.width * rect.width) + (rect.height * rect.height)

)

}

const dim = (max * 2) + 'px'

return {

width: dim,

height: dim,

// 通过margin控制波纹中心点和点击点一致

'margin-left': -max + x + 'px',

'margin-top': -max + y + 'px'

}

}

}

使用

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

// listItem.vue

@touchcancel="hover = false" class="mu-item-wrapper" :class="{'hover': hover}">

// ...

.mu-item-wrapper {

display: block;

color: inherit;

position: relative;

}

最后

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

vue 圆形 水波_vue 开发波纹点击特效组件相关推荐

  1. vue 圆形 水波_vue项目百度地图+echarts的涟漪水波效果

    先看效果 image.png id="allmap" class="map" ref="map" > import echarts f ...

  2. vue 圆形 水波_vuejs 水波纹框架

    Touch ripple component for Vue.js(1.x ~ 2.x). 组件component/指令directive两种方法可供使用,灵活简单,兼容Vue.js全版本. 要注意的 ...

  3. Vue textarea 和input 开发ios点击空白地方键盘不收起问题

    vue h5 ios点击屏幕不收起键盘 --链接 Vue textarea 和input 开发ios点击空白地方键盘不收起问题 1.创建一个index.js文件 //index.js//解决ios点击 ...

  4. axure 元件_在Axure中实现波纹点击特效按钮的方法

    一般按钮都会设计一个点击动画作为反馈,提醒用户点击成功了.下面是一种波纹动画效果,接下来我们看一下在Axure中怎么实现这种点击效果. 基本原理就是设计一个隐藏的半透明的圆,在鼠标点击的时候将其移动到 ...

  5. vue ui无效_vue开发中,父组件添加scoped之后。解决在父组件中无法修改子组件样式问题。...

    在vue的开发中,我们需要引用子组件,包括ui组件(element.iview).但是在父组件中添加scoped之后,在父组件中书写子组件的样式是无效果的.去掉scoped之后,样式可以覆盖.但这样会 ...

  6. jq监听子元素被点击_vue开发app点击字母展示地区列表(兄弟组件之间联动)

    下图这种地区搜索方式在很多app中都很常见,今天就使用vue框架中的 better-scroll 第三方包来实现页面滚动和点击侧边栏字母该字母开头的地区列表置顶功能. 1.A子组件通过使用 this. ...

  7. echart vue 图表大小_vue之将echart封装为组件

    最近的新项目里,有大量数据图表类的需求,为了增强代码的复用性,减少冗余,我开始思考如何将echart封装为组件调用.本文将会以雷达图为案例,一步步讲解在vue项目中如何使用echart,如何将其封装为 ...

  8. css 点击效果_使用CSS实现逼真的水波纹点击效果

    这篇文章特别介绍如何使用CSS来完成水波纹的效果. div的层层叠叠 虽然webkit具有遮罩的能力(webkit mask),不过webkit虽然强大,但在跨浏览器上总是它的罩门,况且在性能上也是往 ...

  9. 点击水波纹效果html5,使用CSS实现逼真的水波纹点击效果

    这篇文章特别介绍如何使用CSS来完成水波纹的效果. div的层层叠叠 虽然webkit具有遮罩的能力(webkit mask),不过webkit虽然强大,但在跨浏览器上总是它的罩门,况且在性能上也是往 ...

最新文章

  1. VMware12.0PRo 下安装Ubuntu14.04LTS
  2. ubuntu/Linux下 提示“conda:未找到命令”
  3. 【Linux部署】第一次部署ZooKeeper启动两次报错问题解决 Could not find or load main class+Failed to bind to /0.0.0.0:8080
  4. python 并发访问数据库_【数据库】如何实现python3实现并发访问水平切分表
  5. atoi(),函数,将字符串转为整形数字
  6. linux之如何复制文件夹所有文件到其它文件夹
  7. git 2015-5-26
  8. 北京上海等昨天大规模断网 专家称与太阳无关
  9. 在线答题助手c语言源码,开源的在线答题小程序
  10. ipad一直卡在白苹果_近万字多图带你玩转iPad——iPad指南
  11. php中怎么缩放图片,php中图片的缩放比例-php教程
  12. 使用python来完成数据的线性拟合
  13. leaflet虚线(leaflet篇.60)
  14. laravel whereDoesntHave 查询不等于条件的数据
  15. html幸运数字游戏,十二生肖的幸运数字
  16. MySQL基础(一)
  17. 我想要申请一个ICP经营许可证,哪个平台能办理呢?
  18. 【MidJourney】初体验——账号注册与基本操作
  19. RFID门禁系统快速识别车辆管理
  20. win10查看计算机系统版本,Win10版本怎么看?Win10系统版本查看方法

热门文章

  1. 用户变量和系统变量的区别_环境变量的用户变量与系统变量的区别
  2. ssh 免密_大数据时代:SSH如何免密码登录?
  3. 5绘制收银台程序_进来学知识了:实操之C/C++的绘制
  4. 力扣 ---- java
  5. html toast屏幕中间,Toast.html
  6. Java把时间毫秒数转换成日期和时间
  7. Android开发笔记(一百一十七)app省电方略
  8. linux git ssh 配置文件,git bash配置ssh 登录 Linux的方法
  9. Selenium 对元素element的操作举例
  10. 子慕谈设计模式系列(二)——设计模式六大原则