wxml
<view class="fly-ball"
hidden="{{ hidden }}"
animation="{{ animation }}"
style="top:{{ top }}px;left:{{ left }}px"></view>
wxss
.fly-ball {
30rpx;
height: 30rpx;
position: fixed;
border-radius: 100%;
z-index: 100;
background: red;
top: 200px;
left: 10px;
display: block;
box-shadow: 0 1px 1px #000, 0 0 3px #fff inset;
}
JS
import { bezier } from "../../utils/util";
const app = getApp()
Component({
/**
* 组件的属性列表
*/
properties: {},
/**
* 组件的初始数据
*/
data: {
loading: false, // 动画是否进行中
animation: {},
hidden: true,
top: 0,
left: 0
},
/**
* 组件的方法列表
*/
methods: {
// 动画
ballAnimation(e) {
// 节流
if (this.data.loading) {
return
}
let topPoint = {},
finger = {},
busPos = {
x: 250,
y: app.globalData.screenHeight
}
finger.x = e.touches[0].clientX // 点击的位置
finger.y = e.touches[0].clientY
this.setData({
top: finger.y,
left: finger.x,
hidden: false,
loading: true
})
// 控制点y值定在低的点上方250px处
if (finger.y < busPos.y) {
topPoint.y = finger.y - 250
} else {
topPoint.y = busPos.y - 250
}
// 控制点确保x在点击点和购物车间
if (finger.x > busPos.x) {
topPoint.x = (finger.x - busPos.x) / 2 + busPos.x
} else {
topPoint.x = (busPos.x - finger.x) / 2 + finger.x
}
let linePos = bezier([busPos, topPoint, finger], 10),
startPoint = linePos[linePos.length - 1]
let animation = wx.createAnimation({
duration: 40
})
for (let i = linePos.length - 1; i >= 0; i--) {
// 根据坐标值逐个计算各点的translate
let startPointX = startPoint.x,
startPointY = startPoint.y,
nextPointX = linePos[i].x,
nextPointY = linePos[i].y,
x = nextPointX - startPointX,
y = nextPointY - startPointY,
scale = 1
if (i < 2) {
scale = 0.6
}
animation.translateX(x).translateY(y).scale(scale).step()
}
this.setData({
animation: animation.export()
})
setTimeout(() => {
this.setData({
loading: false,
animation: {
actions: [{
animates: [{ type: "translateX", args: [0] }, {
type: "translateY",
args: [0]
}, { type: "scale", args: [0, 0] }],
option: {
transition: { duration: 0 }
}
}]
}
})
}, 800)
}
}
})
二次贝塞尔曲线
util.js
function bezier(pots, amount) {
let pot,
lines,
ret = [],
points
for (let i = 0; i <= amount; i++) {
points = pots.slice(0) // 购物车坐标、控制点坐标、点击坐标
lines = []
while (pot = points.shift()) {
if (points.length) {
lines.push(pointLine([pot, points[0]], i / amount))
} else if (lines.length > 1) {
points = lines
lines = []
} else {
break
}
}
ret.push(lines[0])
}
function pointLine(points, rate) {
let pointA, pointB, pointDistance, xDistance, yDistance, tan, radian, tmpPointDistance
let ret = []
pointA = points[0]
pointB = points[1]
xDistance = pointB.x - pointA.x
yDistance = pointB.y - pointA.y
pointDistance = Math.pow(Math.pow(xDistance, 2) + Math.pow(yDistance, 2), 1 / 2) // 斜边长
tan = yDistance / xDistance
radian = Math.atan(tan)
tmpPointDistance = pointDistance * rate
if (pointA.x < pointB.x) {
ret = {
x: pointA.x + tmpPointDistance * Math.cos(radian),
y: pointA.y + tmpPointDistance * Math.sin(radian)
}
} else {
ret = {
x: pointA.x - tmpPointDistance * Math.cos(radian),
y: pointA.y - tmpPointDistance * Math.sin(radian)
}
}
return ret
}
return ret
}
调用
this.selectComponent("#flyBall").ballAnimation(e)
代码主要是抄考网上大神的,改动的地方只是把定时器换成了小程序支持的Animation并且可左右飞入
核心是二次贝塞尔曲线的计算,这个有机会要研究下...