二阶贝塞尔曲线 bezier(),在原作者的基础上加了从右向左的曲线计算
app.js
App({
globalData:{
},
onLaunch: function (){
},
bezier: function (points, times) {
// 0、以3个控制点为例,点A,B,C,AB上设置点D,BC上设置点E,DE连线上设置点F,则最终的贝塞尔曲线是点F的坐标轨迹。
// 1、计算相邻控制点间距。
// 2、根据完成时间,计算每次执行时D在AB方向上移动的距离,E在BC方向上移动的距离。
// 3、时间每递增100ms,则D,E在指定方向上发生位移, F在DE上的位移则可通过AD/AB = DF/DE得出。
// 4、根据DE的正余弦值和DE的值计算出F的坐标。
// 从左到右,正向;从右到左,负向
const DIRECTION = (points[2]['x'] - points[0]['x'])/Math.abs(points[2]['x'] - points[0]['x'])
// 邻控制AB点间距
var bezier_points = [];
var points_D = [];
var points_E = [];
const DIST_AB = Math.sqrt(Math.pow(points[1]['x'] - points[0]['x'], 2) + Math.pow(points[1]['y'] - points[0]['y'], 2));
// 邻控制BC点间距
const DIST_BC = Math.sqrt(Math.pow(points[2]['x'] - points[1]['x'], 2) + Math.pow(points[2]['y'] - points[1]['y'], 2));
// D每次在AB方向上移动的距离
const EACH_MOVE_AD = DIST_AB / times * DIRECTION;
// E每次在BC方向上移动的距离
const EACH_MOVE_BE = DIST_BC / times * DIRECTION;
// 点AB的正切
const TAN_AB = (points[1]['y'] - points[0]['y']) / (points[1]['x'] - points[0]['x']);
// 点BC的正切
const TAN_BC = (points[2]['y'] - points[1]['y']) / (points[2]['x'] - points[1]['x']);
// 点AB的弧度值
const RADIUS_AB = Math.atan(TAN_AB);
// 点BC的弧度值
const RADIUS_BC = Math.atan(TAN_BC);
// 每次执行
for (var i = 1; i <= times; i++) {
// AD的距离
var dist_AD = EACH_MOVE_AD * i;
// BE的距离
var dist_BE = EACH_MOVE_BE * i;
// D点的坐标
var point_D = {};
point_D['x'] = dist_AD * Math.cos(RADIUS_AB) + points[0]['x'];
point_D['y'] = dist_AD * Math.sin(RADIUS_AB) + points[0]['y'];
points_D.push(point_D);
// E点的坐标
var point_E = {};
point_E['x'] = dist_BE * Math.cos(RADIUS_BC) + points[1]['x'];
point_E['y'] = dist_BE * Math.sin(RADIUS_BC) + points[1]['y'];
points_E.push(point_E);
// 此时线段DE的正切值
var tan_DE = (point_E['y'] - point_D['y']) / (point_E['x'] - point_D['x']);
// tan_DE的弧度值
var radius_DE = Math.atan(tan_DE);
// 此时DE的间距
var dist_DE = Math.sqrt(Math.pow((point_E['x'] - point_D['x']), 2) + Math.pow((point_E['y'] - point_D['y']), 2));
// 此时DF的距离
var dist_DF = (dist_AD / DIST_AB) * dist_DE;
// 此时F点的坐标
var point_F = {};
point_F['x'] = dist_DF * Math.cos(radius_DE) + point_D['x'];
point_F['y'] = dist_DF * Math.sin(radius_DE) + point_D['y'];
bezier_points.push(point_F);
}
console.log('bezier_points',bezier_points);
return {
'bezier_points': bezier_points
};
},
})
index.js
//方便引用自身
const app = getApp()
var that
Page({
data: {
//购物车x坐标
animationx: 0,
//购物车y坐标
animationy: 0,
//是否显示飞行物,默认不显示
showdot: false,
//动画对象
ani: {},
//商品记数
count: 0
},
onLoad(options) {
that = this
},
onReady() {
//页面渲染完后获取购物车在页面中的坐标
const query = wx.createSelectorQuery()
query.select('#shopcar').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(function (res) {
let point = res[0]
//坐标修正,让飞行物可以正好落在购物车正中心,20是飞行物宽度一半然后转化成px
var xtemp = (point.left + point.right) / 2 - 20 / 750 * wx.getSystemInfoSync().windowWidth
var ytemp = (point.top + point.bottom) / 2 - 20 / 750 * wx.getSystemInfoSync().windowWidth
console.log('xtemp : ' + xtemp + ' ytemp : ' + ytemp)
that.setData({
//获取修正后坐标
animationx: xtemp,
animationy: ytemp
})
})
},
addshopcar(e) {
// if(that.data.showdot == true){
// return
// }
//获取点击点坐标
var touches = e.touches[0]
//坐标修正,同上,这么做是为了让飞行点落到点击的中心
let toptemp = touches.clientY - 20 / 750 * wx.getSystemInfoSync().windowWidth
let lefttemp = touches.clientX - 20 / 750 * wx.getSystemInfoSync().windowWidth
var animation1 = wx.createAnimation({
duration: 1,
timingFunction: 'ease'
})
//通过极短的时间让飞行点移动到手指点击位置,同时让飞行点显示出来
animation1.left(lefttemp).top(toptemp).step()
// 新方法
// that.animate('#dot', [
// {left:lefttemp+'px',top:toptemp+'px'}
// ], 1, function () {
// that.clearAnimation('#dot', { opacity: true, rotate: true }, function () {
// console.log("清除了#container上的动画属性")
// })
// }.bind(that))
that.setData({
ani: animation1.export(),
showdot: true
})
let points = [
{
x: lefttemp,
y: toptemp
},
{
x: (lefttemp + this.data.animationx) / 2,
y: 100
},
{
x: this.data.animationx,
y: this.data.animationy
},
]
this.linePos1 = app.bezier(points, 30)['bezier_points'];
setTimeout(() => {
this.startAnimation()
}, 100)
},
//开始动画
startAnimation: function () {
var index = 0,
that = this,
bezier_points = that.linePos1;
const animation = wx.createAnimation({
duration: 1,
timingFunction: 'ease'
}, )
let arr = []
//通过Animation的left和top这两个API,将飞行点移动到购物车坐标处
bezier_points.forEach(item => {
animation.left(item.x).top(item.y).step()
// arr.push({left:item.x+'px',top:item.y+'px'})
})
// 新方法
// that.animate('#dot', arr, 1, function () {
// that.clearAnimation('#dot', { opacity: 0, rotate: true }, function () {
// console.log("清除了#container上的动画属性")
// that.setData({showdot:false})
// })
// }.bind(that))
that.setData({
ani: animation.export()
})
},
animationend(e) {
console.log(e);
}
})
index.wxml
<view class="container">
<view wx:for='12345678901234567' class="item" bindtap="addshopcar" hover-class="hover">商品{{index}}</view>
</view>
<view class="dot" id="dot" animation='{{ani}}' bind:animationend="animationend" hidden="{{!showdot}}">+1</view>
<view class="shopcar" id="shopcar">
<image src="https://oss.suning.com/nscmfs/nsimg/68205d3e2c084547b5cac8710ffdb094/super-vip.png" class="shopcarimg"></image>
</view>
index.wxss
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
}
.item {
width: 720rpx;
height: 100rpx;
display: flex;
flex-direction: row;
align-items: center;
font-weight: bold;
padding-left: 30rpx;
border-bottom: 1rpx solid #efeff4;
}
.hover {
background: #d9d9db;
}
.dot {
top: 0;
left: 0;
position: fixed;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: #1dae01;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 24rpx;
color: white;
}
.shopcar {
position: fixed;
right: 200rpx;
bottom: 100rpx;
}
.shopcarimg {
width: 48rpx;
height: 48rpx;
}
.count {
color: red;
position: fixed;
font-weight: bold;
right: 300rpx;
bottom: 100rpx;
}