抛出小球加到地方对应元素晃动(加入购物车动画)
/*
* 添加到购物车动画 脚本,涉及到键盘弹出时,动画需要延迟100ms执行,以保证键盘已经弹回去,页面元素位置不变
* */
/** *
* 动画轨迹控制
* @param addBtnDom 增加按钮的dom元素或者选择器
* @param shopCarDom 购物车的dom元素或选择器
*/
export default function controllPath(addBtn, shopCar, shopImg) {
let addBtnDom = null
let shopCarDom = null
if (typeof addBtn === 'string') {
addBtnDom = document.querySelector(addBtn)
} else if (addBtn instanceof HTMLElement) {
addBtnDom = addBtn
} else {
console.error('加入购物车动画传入的参数错误: 第一个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
if (typeof shopCar === 'string') {
shopCarDom = document.querySelector(shopCar)
} else if (shopCar instanceof HTMLElement) {
shopCarDom = shopCar
} else {
console.error('加入购物车动画传入的参数错误: 第二个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
// 获取两个dom的位置
const addBtnposition = addBtnDom.getBoundingClientRect()
const shopCarPosition = shopCarDom.getBoundingClientRect()
const addBtnCenterX = (addBtnposition.left + addBtnposition.right) / 2
const addBtnCenterY = (addBtnposition.top + addBtnposition.bottom) / 2
const shopCarCenterX = (shopCarPosition.left + shopCarPosition.right) / 2
// const shopCarCenterY = (shopCarPosition.top + shopCarPosition.bottom) / 2
// 计算增加按钮 是在 相对于购物车的 左边还是右边(用于控制后面的移动方向)
const relativePosition = addBtnCenterX > shopCarCenterX ? -1 : 1
// 获取连个dom之间的距离
const xDistance = Math.abs(addBtnCenterX - shopCarCenterX)
// const yDistance = Math.abs(addBtnCenterY - shopCarCenterY)
// 绘制小球并设置其位置
const ballDom = drawBall()
ballDom.style.top = addBtnCenterY + 'px'
ballDom.style.left = addBtnCenterX + 'px'
document.body.appendChild(ballDom)
/*
* 根据一元二次方程的轨迹求出对象的系数 y = ax^2 + bx + c
* let coefficientC = 0;
* let coefficientB = 0;
* let coefficientA = yDistance / Math.pow(xDistance, 2);
*/
// 小球的横竖坐标
let xAbscissa = 0; let yAbscissa = 0
const addCount = xDistance / 10 // 计算变化次数
let curCount = 0 // 当前变化次数
const onceY = 40 / (addCount / 2) // 每次Y轴移动大小
// 设置移动路径
const ballTimer = setInterval(function() {
curCount += 1
// 每次重新坐标
xAbscissa += 10 * relativePosition
// if ()
// yAbscissa = (yDistance / Math.pow(xDistance, 2)) * Math.pow(xAbscissa, 2)
if (curCount <= addCount / 1.5) {
yAbscissa -= onceY
} else {
yAbscissa += onceY
}
ballDom.style.top = addBtnCenterY + yAbscissa + 'px'
ballDom.style.left = addBtnCenterX + xAbscissa + 'px'
// 检查是否到达终点
const surplusDistance = parseInt(ballDom.style.left) - shopCarCenterX
// const surplusDistanceY = parseInt(ballDom.style.top) - shopCarCenterY
if (Math.abs(surplusDistance) <= 10) {
clearInterval(ballTimer)
document.body.removeChild(ballDom)
let shopImgDom = null // 购物车图片
if (typeof shopImg === 'string') {
shopImgDom = document.querySelector(shopImg)
} else if (shopImg instanceof HTMLElement) {
shopImgDom = shopImg
} else {
console.error('加入购物车动画传入的参数错误: 第三个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
let rotateCount = 4 // 购物车图片左右晃动次数
let rotateVal = 0 // 晃动角度
const rotateInterval = setInterval(() => {
if (rotateCount < 0) clearInterval(rotateInterval)
else rotateCount--
if (rotateCount === -1) rotateVal = 0 // 结束时摆正
else rotateVal = 10 * Math.pow(-1, rotateCount) // 晃动角度10,一正一负
shopImgDom.style.transform = 'rotate(' + rotateVal + 'deg)' // 设置css样式
}, 100)
}
}, 50)
}
/*
* 绘制小球
* */
function drawBall() {
const ballDom = document.createElement('div')
ballDom.style.width = '20px'
ballDom.style.height = '20px'
ballDom.style.border = '1px solid #ccc'
ballDom.style.background = '#fa5151'
ballDom.style.borderRadius = '50%'
ballDom.style.position = 'absolute'
ballDom.style.zIndex = 99999
return ballDom
}
/** *
* 动画轨迹控制
* @param addBtnDom 增加按钮的dom元素或者选择器
* @param shopCarDom 购物车的dom元素或选择器
*/
export const controllGiftPath = (addBtn, shopCar, shopImg, scrollT) => {
let addBtnDom = null
let shopCarDom = null
if (typeof addBtn === 'string') {
addBtnDom = document.querySelector(addBtn)
} else if (addBtn instanceof HTMLElement) {
addBtnDom = addBtn
} else {
console.error('加入购物车动画传入的参数错误: 第一个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
if (typeof shopCar === 'string') {
shopCarDom = document.querySelector(shopCar)
} else if (shopCar instanceof HTMLElement) {
shopCarDom = shopCar
} else {
console.error('加入购物车动画传入的参数错误: 第二个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
// 获取两个dom的位置
const addBtnposition = addBtnDom.getBoundingClientRect()
const shopCarPosition = shopCarDom.getBoundingClientRect()
const addBtnCenterX = (addBtnposition.left + addBtnposition.right) / 2
const addBtnCenterY = (addBtnposition.top + addBtnposition.bottom) / 2 + scrollT
const shopCarCenterX = (shopCarPosition.left + shopCarPosition.right) / 2
const shopCarCenterY = (shopCarPosition.top + shopCarPosition.bottom) / 2
// 计算增加按钮 是在 相对于购物车的 左边还是右边(用于控制后面的移动方向)
const relativePosition = addBtnCenterX > shopCarCenterX ? -1 : 1
const relativePositionY = addBtnCenterY > shopCarCenterY ? -1 : 1
// 获取连个dom之间的距离
const xDistance = Math.abs(addBtnCenterX - shopCarCenterX)
const yDistance = Math.abs(addBtnCenterY - shopCarCenterY)
// 绘制小球并设置其位置
const ballDom = drawBall2()
ballDom.style.top = addBtnCenterY - 10 * relativePosition + 'px'
ballDom.style.left = addBtnCenterX - 10 * relativePositionY + 'px'
document.body.appendChild(ballDom)
/*
* 根据一元二次方程的轨迹求出对象的系数 y = ax^2 + bx + c
* let coefficientC = 0;
* let coefficientB = 0;
* let coefficientA = yDistance / Math.pow(xDistance, 2);
*/
// 小球的横竖坐标
let xAbscissa = 0; let yAbscissa = 0
const allTime = 600
const stepTime = 50
const moveCount = allTime / stepTime
const onceX = xDistance / moveCount
const onceY = yDistance / moveCount
// const step = 25
// const addCount = xDistance / step // 计算变化次数
// const onceY = yDistance / addCount // 每次Y轴移动大小
// 设置移动路径
const ballTimer = setInterval(function() {
// 每次重新坐标
xAbscissa += onceX * relativePosition
yAbscissa += onceY * relativePositionY
ballDom.style.top = addBtnCenterY + yAbscissa + 'px'
ballDom.style.left = addBtnCenterX + xAbscissa + 'px'
// 检查是否到达终点
const surplusDistance = parseInt(ballDom.style.left) - shopCarCenterX
// const surplusDistanceY = parseInt(ballDom.style.top) - shopCarCenterY
if (Math.abs(surplusDistance) <= 10) {
clearInterval(ballTimer)
document.body.removeChild(ballDom)
let shopImgDom = null // 购物车图片
if (typeof shopImg === 'string') {
shopImgDom = document.querySelector(shopImg)
} else if (shopImg instanceof HTMLElement) {
shopImgDom = shopImg
} else {
console.error('加入购物车动画传入的参数错误: 第三个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
let rotateCount = 4 // 购物车图片左右晃动次数
let rotateVal = 0 // 晃动角度
const rotateInterval = setInterval(() => {
if (rotateCount < 0) clearInterval(rotateInterval)
else rotateCount--
if (rotateCount === -1) rotateVal = 0 // 结束时摆正
else rotateVal = 10 * Math.pow(-1, rotateCount) // 晃动角度10,一正一负
shopImgDom.style.transform = 'rotate(' + rotateVal + 'deg)' // 设置css样式
}, 100)
}
}, stepTime)
}
/*
* 绘制小球
* */
function drawBall2() {
const ballDom = document.createElement('div')
ballDom.style.width = '16px'
ballDom.style.height = '16px'
ballDom.style.background = '#fa5151'
ballDom.style.borderRadius = '50%'
ballDom.style.position = 'absolute'
ballDom.style.zIndex = 99999
return ballDom
}
/** *
* 动画轨迹控制
* 抛物线:利用抛物线方程 y = ax^2 + bx + c 传入3点坐标(x1, y1)(x2, y2)(x3, y3)求解出参数 a, b, c
a: (y3 - y1 + (y2 - y1) * (x1 - x3) / (x2 - x1)) / (x3 * x3 - x1 * x1 - (x3 - x1) * (x1 + x2))
b: (y2 - y1 + a * x1 * x1 - a * x2 * x2) / (x2 - x1)
c: y1 - a * x1 * x1 - b * x1
从起点到终点, x匀速增加, 将x 带入方程得到y值,(x, y)即为动画每一帧的坐标位置
* @param addBtnDom 增加按钮的dom元素或者选择器
* @param shopCarDom 购物车的dom元素或选择器
*/
export const controllGiftPath2 = (addBtn, shopCar, shopImg, scrollT) => {
let addBtnDom = null
let shopCarDom = null
if (typeof addBtn === 'string') {
addBtnDom = document.querySelector(addBtn)
} else if (addBtn instanceof HTMLElement) {
addBtnDom = addBtn
} else {
console.error('加入购物车动画传入的参数错误: 第一个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
if (typeof shopCar === 'string') {
shopCarDom = document.querySelector(shopCar)
} else if (shopCar instanceof HTMLElement) {
shopCarDom = shopCar
} else {
console.error('加入购物车动画传入的参数错误: 第二个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
// 获取两个dom的位置
const addBtnposition = addBtnDom.getBoundingClientRect()
const shopCarPosition = shopCarDom.getBoundingClientRect()
// const addBtnCenterX = (addBtnposition.left + addBtnposition.right) / 2
// const addBtnCenterY = (addBtnposition.top + addBtnposition.bottom) / 2 + scrollT
// const shopCarCenterX = (shopCarPosition.left + shopCarPosition.right) / 2
// const shopCarCenterY = (shopCarPosition.top + shopCarPosition.bottom) / 2
const addBtnCenterX = addBtnposition.left
const addBtnCenterY = addBtnposition.top
const shopCarCenterX = (shopCarPosition.left + shopCarPosition.right) / 2
const shopCarCenterY = shopCarPosition.top
// 计算增加按钮 是在 相对于购物车的 左边还是右边(用于控制后面的移动方向)
const [x1, x2, y1, y2] = [parseInt(addBtnCenterX), parseInt(shopCarCenterX), parseInt(addBtnCenterY), parseInt(shopCarCenterY)]
// console.log('x1: ', x1, 'x2: ', x2, 'y1: ', y1, 'y2: ', y2)-
const x3 = (x1 + x2) * 4 / 3
const y3 = y2 - 100
const allTime = 300
const stepTime = 30
const speedX = (x2 - x1) * stepTime / allTime
let times = 0
const a = (y3 - y1 + (y2 - y1) * (x1 - x3) / (x2 - x1)) / (x3 * x3 - x1 * x1 - (x3 - x1) * (x1 + x2))
const b = (y2 - y1 + a * x1 * x1 - a * x2 * x2) / (x2 - x1)
const c = y1 - a * x1 * x1 - b * x1
// console.log('a: ', a, 'b: ', b, 'c: ', c)
// const relativePosition = addBtnCenterX > shopCarCenterX ? -1 : 1
// const relativePositionY = addBtnCenterY > shopCarCenterY ? -1 : 1
// // 获取连个dom之间的距离
// const xDistance = Math.abs(addBtnCenterX - shopCarCenterX)
// const yDistance = Math.abs(addBtnCenterY - shopCarCenterY)
// 绘制小球并设置其位置
const ballDom = drawBall2()
ballDom.style.top = y1 + 'px'
ballDom.style.left = x1 + 'px'
document.body.appendChild(ballDom)
const timer = setInterval(() => {
times += 1
const x = x1 + speedX * times
const y = a * x * x + b * x + c
if (times * stepTime > allTime) {
clearInterval(timer)
document.body.removeChild(ballDom)
let shopImgDom = null // 购物车图片
if (typeof shopImg === 'string') {
shopImgDom = document.querySelector(shopImg)
} else if (shopImg instanceof HTMLElement) {
shopImgDom = shopImg
} else {
console.error('加入购物车动画传入的参数错误: 第三个参数应为增加按钮的dom元素或该元素的选择器。')
return
}
let rotateCount = 4 // 购物车图片左右晃动次数
let rotateVal = 0 // 晃动角度
const rotateInterval = setInterval(() => {
if (rotateCount < 0) clearInterval(rotateInterval)
else rotateCount--
if (rotateCount === -1) rotateVal = 0 // 结束时摆正
else rotateVal = 10 * Math.pow(-1, rotateCount) // 晃动角度10,一正一负
shopImgDom.style.transform = 'rotate(' + rotateVal + 'deg)' // 设置css样式
}, 100)
} else {
// console.log('x: ', x, 'y: ', y)
ballDom.style.left = `${x}px`
ballDom.style.top = `${y}px`
}
}, stepTime)
}
- 传入的dom参数的样式需要自己先写好,抛出的路线和晃动次数与角度可以自己调整