
上面是效果圖,具體代碼如下:
html
<template>
<view id="test">
<view>
<!-- 内容 -->
<view class="flex">
<scroll-view scroll-y scroll-with-animation style="height:calc(100vh - 400upx)">
<!-- 左邊 -->
<view class="flex-sub bg-pink">
<view :class="index == curListIndex ? 'true': 'false'" @tap="goAnchor" :data-id="item.id" class="padding-tb-sm" v-for="(item, index) in leftNav" :key="index">
<text>{{item.name}}</text>
</view>
</view>
</scroll-view>
<scroll-view scroll-y scroll-with-animation style="height:calc(100vh - 400upx)" :scroll-into-view="'main-'+ mainCur" @scroll="noScroll">
<!-- 右邊 -->
<view class="flex-sub bg-red">
<view v-for="(item, index) in rightNav" :key="index" :id="'main-'+ index">
<view style="height: 260upx; background-color: #023C41;">
<view>
<text>{{item.name}}</text>
</view>
<view class="flex justify-end text-center text-content">
<view class="item bg-blue"
:class="rightListSum[index].delAnimation ? 'delRightShoppingCardAnimations' : 'delLeftShoppingCardAnimations'"
v-if="rightListSum[index].showDel" @click="delShoppingCard(index)">
<text>-</text>
</view>
<view style="width: 60upx;" v-if="rightListSum[index].showDel">
<text>{{rightListSum[index].sum}}</text>
</view>
<view class="item bg-blue" :data-index="index" @click="addShoppingCard">
<text>+</text>
</view>
</view>
</view>
<view class="gray_big_line"></view>
</view>
</view>
</scroll-view>
<!-- 小球動畫 -->
<view :animation="animationY" :style="'position:fixed;top:' + ballY + 'px;'" v-if="showBall">
<view class="ball" :animation="animationX" :style="'position:fixed;left:' + ballX + 'px;'">
<image src="../../static/james.jpeg" mode=""></image>
</view>
</view>
</view>
<!-- 底部導航欄 -->
<view style="position: fixed; bottom: 0; height: 85upx; width: 100%; background-color: #CCCCCC;">
<view v-if="showShoppingCart" :class="shoppingCartAnimation? 'shoppingCartAnimations' : '' " style="margin-left: 64rpx;width: 85upx;height: 85upx; border-radius: 100%;background-color: red;">
</view>
</view>
</view>
</view>
</template>
script
<script>
export default {
data() {
return {
rightListSum: [
{
showDel: false,
delAnimation: false, // 删除動畫
sum: 0,
}
],
alreadyLike: require('@/static/james.jpeg'),
showShoppingCart: true, // 購物車顯示
shoppingCartAnimation: false, // 購物車動畫
showBall: false, // 小球是否顯示
animationY: '', // 動畫Y
animationX: '', // 動畫X
ballY: '', // 小球目前位置Y
ballX: '', // 小球目前位置X
leftNav: [
{
name: 'leftNav菜單1',
id: 0
},
{
name: 'leftNav菜單2',
id: 1
},
{
name: 'leftNav菜單3',
id: 2
},
{
name: 'leftNav菜單4',
id: 3
},
{
name: 'leftNav菜單5',
id: 4
}
],
rightNav: [
{
name: 'rightNav菜單1',
id: 0
},
{
name: 'rightNav菜單2',
id: 1
},
{
name: 'rightNav菜單3',
id: 2
},
{
name: 'rightNav菜單4',
id: 3
},
{
name: 'rightNav菜單5',
id: 4
},
{
name: 'rightNav菜單6',
id: 5
},
{
name: 'rightNav菜單7',
id: 6
}
],
curListIndex: 0,
mainCur: 0,
listHeight: [], // 左邊清單高度
scrollY: ''
}
},
onLoad(option) {
console.log('onLoad')
this.calculateHeight()
},
onReady() {
// 接口傳回
// this.calculateHeight()
},
onShow() {
},
methods: {
// 删除購物車
async delShoppingCard(index) {
console.log(index)
if (this.rightListSum[index].sum-1 <= 0) {
this.rightListSum[index].sum = this.rightListSum[index].sum - 1
this.rightListSum[index].delAnimation = false
this.setDelayTime(100).then(() => {
this.rightListSum[index].showDel = false
})
} else {
this.rightListSum[index].sum = this.rightListSum[index].sum - 1
}
},
// 添加購物車
addShoppingCard(e){
console.log(e)
let index = e.currentTarget.dataset.index
this.rightListSum[index].sum = this.rightListSum[index].sum + 1
this.rightListSum[index].delAnimation = true
this.rightListSum[index].showDel = true
// x, y表示手指點選橫縱坐标, 即小球的起始坐标
let ballX = e.detail.x,
ballY = e.detail.y;
this.createAnimation(ballX, ballY);
},
// 延遲執行
setDelayTime(sec) {
return new Promise((resolve, reject) => {
setTimeout(() => {resolve()}, sec)
});
},
// 建立動畫
createAnimation(ballX, ballY) {
uni.getSystemInfo({
success: async (e) => {
// var bottomX = e.windowWidth; // 結束位置X
var bottomX = 50; // 結束位置X
var bottomY = e.windowHeight - 50; // 結束位置Y
var animationX = this.flyX(bottomX, ballX); // 建立小球水準動畫
var animationY = this.flyY(bottomY, ballY); // 建立小球垂直動畫
this.ballX = ballX; // 小球目前位置X
this.ballY = ballY; // 小球目前位置Y
this.showBall = true; // 顯示小球
this.setDelayTime(100).then(() => {
this.animationX = animationX.export(); // 執行動畫X
this.animationY = animationY.export(); // 執行動畫Y
// 400ms延時, 即小球的抛物線時長
return this.setDelayTime(400);
}).then(() => {
this.animationX = this.flyX(0, 0, 0).export(); // 執行動畫X
this.animationY = this.flyY(0, 0, 0).export(); // 執行動畫Y
this.showBall = false; // 隐藏小球
this.shoppingCartAnimation = true // 購物車動畫
// 400ms延時, 即小球的抛物線時長
return this.setDelayTime(400);
}).then(() => {
this.shoppingCartAnimation = false // 購物車動畫
})
}
})
},
// 水準動畫
flyX(bottomX, ballX, duration) {
/**
* bottomX 結束位置
* ballX 開始位置
* duration 動畫持續時間
*/
let animation = uni.createAnimation({
duration: duration || 400,
timingFunction: 'linear',
})
animation.translateX(bottomX-ballX).step(); // 動畫效果
return animation;
},
// 垂直動畫
flyY(bottomY, ballY, duration) {
/**
* bottomY 結束位置
* ballY 開始位置
* duration 動畫持續時間
*/
let animation = uni.createAnimation({
duration: duration || 400,
timingFunction: 'ease-in',
})
console.log(bottomY)
animation.translateY(bottomY-ballY).step(); // 動畫效果
return animation;
},
// 滑動
noScroll(e) {
// console.log(e)
console.log(e.detail.scrollTop)
this.scrollY = e.detail.scrollTop + 20
// 當滾動到頂部
if (this.scrollY < 0) {
this.curListIndex = 0
// this.mainCur = 0
return true
}
// 在中間部分滾動
for (let i = 0; i < this.listHeight.length - 1; i++) {
let height = this.listHeight[i]
// 思路 拿數組裡面的開始、結束 值進行範圍比較
if (this.scrollY > height.start && this.scrollY < height.end) {
console.log('-----------合格----------')
console.log(this.listHeight[i])
this.curListIndex = i
// this.mainCur = i
}
}
},
// 左邊導航欄點選
goAnchor(e) {
console.log(e)
console.log(e.currentTarget.dataset.id)
this.curListIndex = e.currentTarget.dataset.id // 下标選中
this.mainCur = e.currentTarget.dataset.id // 右邊錨點
console.log(this.mainCur)
},
// 計算每個左邊區塊的高度
calculateHeight() {
let list = this.rightNav
let tabHeight = 0;
for (let i = 0; i < list.length; i++) {
const query = uni.createSelectorQuery().in(this);
// console.log(query)
query.select("#main-" + i).boundingClientRect(data => {
// console.log(data);
// console.log(data.height);
let res = {}
res.start = tabHeight // 開始高度
tabHeight = tabHeight + data.height;
res.end = tabHeight // 結束高度
this.listHeight.push(res)
}).exec();
let rightListSum = {
showDel: false,
delAnimation: false, // 删除動畫
sum: 0,
}
this.rightListSum.push(rightListSum)
}
console.log(this.listHeight)
console.log(this.rightListSum)
// let view = uni.createSelectorQuery().select("#main-" + 5);
// console.log(view)
// view.fields({
// size: true
// }, data => {
// console.log(data)
// }).exec();
}
}
}
</script>
style
<style>
.flex{
display: flex;
}
.true {
border: 8rpx solid red;
}
.false {
border: 2rpx solid #ccc;
}
.item {
width:50rpx;
height: 50rpx;
border-radius: 100%;
}
/* 購物車動畫 */
.shoppingCartAnimations {
animation: shoppingCartAnimation 1s;
}
@keyframes shoppingCartAnimation {
0% {
opacity: 0;
transform: scale3d(.3,.3,.3)
}
20% {
transform: scale3d(1.1,1.1,1.1)
}
40% {
transform: scale3d(.9,.9,.9)
}
60% {
opacity: 1;
transform: scale3d(1.03,1.03,1.03)
}
80% {
transform: scale3d(.97,.97,.97)
}
to {
opacity: 1;
transform: scaleX(1)
}
}
.but {
margin: 10rpx;
height: 60rpx;
width: 80rpx;
display: flex;
}
.ball {
height: 40rpx;
width: 40rpx;
border-radius: 50%;
position: fixed;
}
.ball image{
width: 100%;
height: 100%;
}
/* 從右到左 */
.delRightShoppingCardAnimations {
animation: delRightShoppingCardAnimation .5s;
}
@keyframes delRightShoppingCardAnimation {
from {
transform: translateX(100rpx) rotate(900deg);
animation-timing-function: linear;
}
to {
transform: translateX(0rpx) rotate(0);
}
}
/* 從左到右 */
.delLeftShoppingCardAnimations {
animation: delLeftShoppingCardAnimation .5s;
}
@keyframes delLeftShoppingCardAnimation {
from {
transform: translateX(0rpx) rotate(0);
}
to {
transform: translateX(100rpx) rotate(900deg);
animation-timing-function: linear;
}
}
</style>