欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue實(shí)現(xiàn)列表滾動(dòng)的過渡動(dòng)畫

 更新時(shí)間:2020年06月29日 15:12:20   作者:V1nc4nt丶  
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)列表滾動(dòng)的過渡動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

本文實(shí)例為大家分享了Vue實(shí)現(xiàn)列表滾動(dòng)過渡動(dòng)畫的具體代碼,供大家參考,具體內(nèi)容如下

效果圖

失幀比較嚴(yán)重,在手機(jī)上效果更佳。

原理分析

這個(gè)滾動(dòng)頁面由兩個(gè)部分布局(底部固定的Tab頁面除外)。一個(gè)是頂部的banner輪播,一個(gè)是下面的列表。這里的重點(diǎn)是做列表的動(dòng)畫,banner輪播的網(wǎng)上資料很多,請(qǐng)自行查找。

這個(gè)動(dòng)畫最重要的是在滾動(dòng)中實(shí)時(shí)計(jì)算startIndex和endIndex,動(dòng)畫比較簡(jiǎn)單,就是scale和opacity的變化。向下滾動(dòng)時(shí),startIndex變?。幌蛏蠞L動(dòng)時(shí),endIndex變大時(shí),新露臉的項(xiàng)做該動(dòng)畫。當(dāng)滾動(dòng)連起來,就是一個(gè)完整的動(dòng)畫了。

涉及的技術(shù)

使用better-scroll做滾動(dòng)以及輪播圖

使用create-keyframe-animation做動(dòng)畫控制

實(shí)現(xiàn)步驟

1、vue的template部分

注意:由于IOS渲染速度比較快, 必須把沒有展現(xiàn)在首屏的頁面上的item隱藏掉,即index比startIndex小、比endIndex大的item都應(yīng)該隱藏,避免頁面動(dòng)畫混亂。

<div class="area-wrapper" ref="areaWrapper">
 <div v-for="(item, index) in areaList" :key="index"
 @click="clickAreaItem(item.id)"
 :ref="'area-' + index" class="area"
 :style="{ backgroundImage: 'url('+item.thumbUrl+')', 'opacity': (index < startIndex || index > endIndex) ? 0 : 1}">
  <div class="content">
  <h2 class="num">{{item.num}}</h2>
  <div style="vertical-align:text-bottom">
   <p class="name">{{item.name}}</p>
   <p class="desc">{{item.desc}}</p>
  </div>
  </div>
 </div>
</div>

高度預(yù)設(shè)。用于計(jì)算startIndex、endIndex

const AreaItemHeight = 119 // 每一項(xiàng)的高度(這里默認(rèn)一致,如果不一致請(qǐng)自行修改startIndex、endIndex的計(jì)算方式)
const MarginBottom = 15  // 列表項(xiàng)的底部邊距
const TopHeight = 160  // banner的高度
const BottomHeight = 50  // 底部Tab的高度

監(jiān)聽滾動(dòng)。并實(shí)時(shí)計(jì)算startIndex、endIndex

scroll (position) {
 const scrollY = position.y
 if (scrollY < 0) {
  // startIndex計(jì)算
  const currentStartIndex = Math.abs(scrollY) <= TopHeight ? 0 : parseInt((Math.abs(scrollY) - TopHeight) / (AreaItemHeight + MarginBottom))
  // endIndex計(jì)算
  let currentEndIndex = Math.floor((window.innerHeight - (TopHeight + scrollY) - BottomHeight) / (AreaItemHeight + MarginBottom))
  if (currentEndIndex > this.areaList.length - 1) {
   currentEndIndex = this.areaList.length - 1
  }
  // 這里使用vue的watch屬性監(jiān)聽更好
  if (currentStartIndex !== this.startIndex) {
   if (currentStartIndex < this.startIndex) {
    // 運(yùn)行動(dòng)畫
    this.runAnimation(currentStartIndex)
   }
   this.startIndex = currentStartIndex
  }
  // 這里使用vue的watch屬性監(jiān)聽更好
  if (currentEndIndex !== this.endIndex) {
   if (currentEndIndex > this.endIndex) {
   this.runAnimation(currentEndIndex)
   }
   this.endIndex = currentEndIndex
  }
 }
}

運(yùn)行動(dòng)畫

runAnimation (index) {
 animations.registerAnimation({
  name: 'scale',
  animation: [
   {
   scale: 0.5,
   opacity: 0
   },
   {
   scale: 1,
   opacity: 1
   }
  ],
  presets: {
   duration: 300,
   resetWhenDone: true
  }
 })
 animations.runAnimation(this.$refs['area-' + index], 'scale')
}

完整代碼

.vue文件

<template>
<div class="address-wrapper" style="height: 100%;">
 <scroll ref="scroll" class="address-content" :data="areaList" @scroll="scroll" :listen-scroll="listenScroll" :probe-type="probeType" :bounce="false">
 <div>
  <div v-if="bannerList.length" style="position: relative;">
  <slider :list="bannerList">
   <div v-for="item in bannerList" :key="item.id" :style="{height: sliderHeight + 'px'}">
   <img class="needsclick" :src="item.thumbUrl" width="100%" height="100%" />
   </div>
  </slider>
  <div class="banner-bg"></div>
  <div class="banner-bg-1"></div>
  </div>

  <div class="area-wrapper" ref="areaWrapper">
  <div v-for="(item, index) in areaList" :key="index"
  @click="clickAreaItem(item.id)"
  :ref="'area-' + index" class="area"
  :style="{ backgroundImage: 'url('+item.thumbUrl+')', 'opacity': (index < startIndex || index > endIndex) ? 0 : 1}">
   <div class="content">
   <h2 class="num">{{item.num}}</h2>
   <div style="vertical-align:text-bottom">
    <p class="name">{{item.name}}</p>
    <p class="desc">{{item.desc}}</p>
   </div>
   <!-- <div></div> -->
   </div>
  </div>
  </div>
 </div>
 </scroll>
 <router-view />
</div>
</template>

<script>
import Slider from '@/components/slider/slider'
import Scroll from '@/components/scroll/scroll'
import { isIphoneX } from '@/assets/js/brower'
import animations from 'create-keyframe-animation'
import axios from '@/api/axiosApi'
import areaList from '@/assets/json/areaList.json'
import bannerList from '@/assets/json/bannerAddress.json'

// 每一個(gè)的Area的高度,都是一樣的
const AreaItemHeight = 119
const MarginBottom = 15
const TopHeight = 160
const BottomHeight = 50

export default {
 data () {
 return {
  startIndex: 0,
  endIndex: 3,
  bannerList,
  areaList
 }
 },
 components: {
 Slider, Scroll
 },
 created () {
 this.probeType = 3
 this.listenScroll = true
 this.sliderHeight = 210 + 20
 if (isIphoneX()) {
  this.sliderHeight += 34
 }

 this._getBanner()
 this._getAddressList()
 },
 mounted () {
 this.endIndex = Math.floor((window.innerHeight - TopHeight - BottomHeight) / (AreaItemHeight + MarginBottom))
 },
 methods: {
 _getBanner () {
  axios.get(this, '/v1/banner/1', null, (data) => {
  data.forEach(item => {
   item.thumbUrl += '-banner'
  })
  this.bannerList = data
  }, null, false)
 },
 _getAddressList () {
  axios.get(this, '/v1/address/1', {
  pageSize: 30
  }, (data) => {
  // data.forEach(item => {
  // item.thumbUrl += '-tiaomu'
  // })
  this.areaList = data
  }, null, false)
 },
 scroll (position) {
  const scrollY = position.y
  if (scrollY < 0) {
  const currentStartIndex = Math.abs(scrollY) <= TopHeight ? 0 : parseInt((Math.abs(scrollY) - TopHeight) / (AreaItemHeight + MarginBottom))
  let currentEndIndex = Math.floor((window.innerHeight - (TopHeight + scrollY) - BottomHeight) / (AreaItemHeight + MarginBottom))
  if (currentEndIndex > this.areaList.length - 1) {
   currentEndIndex = this.areaList.length - 1
  }

  if (currentStartIndex !== this.startIndex) {
   if (currentStartIndex < this.startIndex) {
   this.runAnimation(currentStartIndex)
   }
   this.startIndex = currentStartIndex
  }
  if (currentEndIndex !== this.endIndex) {
   if (currentEndIndex > this.endIndex) {
   this.runAnimation(currentEndIndex)
   }
   this.endIndex = currentEndIndex
  }
  }
 },
 runAnimation (index) {
  animations.registerAnimation({
  name: 'scale',
  animation: [
   {
   scale: 0.5,
   opacity: 0
   },
   {
   scale: 1,
   opacity: 1
   }
  ],
  presets: {
   duration: 300,
   resetWhenDone: true
  }
  })
  animations.runAnimation(this.$refs['area-' + index], 'scale')
 },
 clickAreaItem (id) {
  this.$router.push(`address/addressDetail/${id}`)
 }
 }
}
</script>

<style lang="stylus" scoped>
.address-wrapper {
 .address-content {
 height: 100%;
 overflow: hidden;

 .banner-bg {
  height: 50px;
  width: 100%;
  position: absolute;
  bottom: -1px;
  background:-moz-linear-gradient(top, rgba(249, 250, 252, 0.3), rgba(249, 250, 252, 1));/*火狐*/
  background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(249, 250, 252, 0.3)), to(rgba(249, 250, 252, 1))); /*谷歌*/
  background-image: -webkit-gradient(linear,left bottom,left top,color-start(0, rgba(249, 250, 252, 0.3)),color-stop(1, rgba(249, 250, 252, 1)));/* Safari & Chrome*/
 }

 .banner-bg-1 {
  height: 20px;
  width: 100%;
  position: absolute;
  bottom: 49px;
  background:-moz-linear-gradient(top, rgba(249, 250, 252, 0), rgba(249, 250, 252, 0.3));/*火狐*/
  background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(249, 250, 252, 0)), to(rgba(249, 250, 252, 0.3))); /*谷歌*/
  background-image: -webkit-gradient(linear,left bottom,left top,color-start(0, rgba(249, 250, 252, 0)),color-stop(1, rgba(249, 250, 252, 0.3)));/* Safari & Chrome*/
 }

 .area-wrapper {
  transform: translateY(-45px)
  padding: 0 15px;
  z-index: 1;

  .area {
  margin-bottom: 15px;
  height: 119px;
  width: 100%;
  border-radius: 10px;
  background-repeat: no-repeat;
  background-size: cover;
  box-shadow: 0 0 10px #a4a3a3;
  display: flex;
  align-items: flex-end;

  .content {
   color: #fff;
   display: flex;
   padding-right: 60px;
   padding-bottom: 15px;
   line-height: 1.2;

   .num {
   bottom: 35px;
   font-size: 48px;
   font-weight: 100;
   padding: 0 15px;
   display:table-cell;
   vertical-align:bottom;
   }

   .name {
   font-size: 21px;
   font-weight: 600;
   line-height: 1.7;
   }

   .desc {
   font-size: 14px;
   }
  }
  }
 }
 }
}
</style>

本地json文件,請(qǐng)自行修改圖片路徑

bannerAddress.json

[
 {
 "id": 1,
 "contentId": 111111,
 "type": 1,
 "thumbUrl": "./static/img/banner/banner_address_1.jpg"
 },
 {
 "id": 2,
 "contentId": 111111,
 "type": 1,
 "thumbUrl": "./static/img/banner/banner_address_2.jpg"
 },
 {
 "id": 3,
 "contentId": 111111,
 "type": 1,
 "thumbUrl": "./static/img/banner/banner_address_3.jpg"
 }
]

areaList.json

[
 {
 "id": "ba062c32fdf611e7ba2d00163e0c27f8",
 "name": "凱里",
 "desc": "這是凱里喲~",
 "num": 17,
 "thumbUrl": "./static/img/area/kaili.png"
 }, {
 "id": "ba5287a7fdf611e7ba2d00163e0c27f8",
 "name": "丹寨",
 "desc": "這是丹寨喲~",
 "num": 8,
 "thumbUrl": "./static/img/area/danzai.png"
 }, {
 "id": "ba9da079fdf611e7ba2d00163e0c27f8",
 "name": "麻江",
 "desc": "這是麻江喲~",
 "num": 12,
 "thumbUrl": "./static/img/area/majiang.png"
 }, {
 "id": "baeb0926fdf611e7ba2d00163e0c27f8",
 "name": "黃平",
 "desc": "這是黃平喲~",
 "num": 7,
 "thumbUrl": "./static/img/area/huangping.png"
 }, {
 "id": "bb357191fdf611e7ba2d00163e0c27f8",
 "name": "施秉",
 "desc": "這是施秉喲~",
 "num": 6,
 "thumbUrl": "./static/img/area/shibing.png"
 }, {
 "id": "bb842d8ffdf611e7ba2d00163e0c27f8",
 "name": "鎮(zhèn)遠(yuǎn)",
 "desc": "這是鎮(zhèn)遠(yuǎn)喲~",
 "num": 3,
 "thumbUrl": "./static/img/area/zhenyuan.png"
 }, {
 "id": "bbce67dffdf611e7ba2d00163e0c27f8",
 "name": "岑鞏",
 "desc": "這是岑鞏喲~",
 "num": 23,
 "thumbUrl": "./static/img/area/cengong.png"
 }, {
 "id": "bc198ca9fdf611e7ba2d00163e0c27f8",
 "name": "三穗",
 "desc": "這是三穗喲~",
 "num": 66,
 "thumbUrl": "./static/img/area/sansui.png"
 }, {
 "id": "bc64498bfdf611e7ba2d00163e0c27f8",
 "name": "天柱",
 "desc": "這是天柱喲~",
 "num": 128,
 "thumbUrl": "./static/img/area/tianzhu.png"
 }, {
 "id": "bcaf466bfdf611e7ba2d00163e0c27f8",
 "name": "錦屏",
 "desc": "這是錦屏喲~",
 "num": 107,
 "thumbUrl": "./static/img/area/jinping.png"
 }, {
 "id": "bcfa6f1bfdf611e7ba2d00163e0c27f8",
 "name": "黎平",
 "desc": "這是黎平喲~",
 "num": 211,
 "thumbUrl": "./static/img/area/liping.png"
 }, {
 "id": "bd44cca9fdf611e7ba2d00163e0c27f8",
 "name": "從江",
 "desc": "這是從江喲~",
 "num": 17,
 "thumbUrl": "./static/img/area/congjiang.png"
 }, {
 "id": "bd8f5cd4fdf611e7ba2d00163e0c27f8",
 "name": "榕江",
 "desc": "這是榕江喲~",
 "num": 17,
 "thumbUrl": "./static/img/area/rongjiang.png"
 }, {
 "id": "bdda2928fdf611e7ba2d00163e0c27f8",
 "name": "雷山",
 "desc": "這是雷山喲~",
 "num": 17,
 "thumbUrl": "./static/img/area/leishan.png"
 }, {
 "id": "be25afc0fdf611e7ba2d00163e0c27f8",
 "name": "臺(tái)江",
 "desc": "這是臺(tái)江喲~",
 "num": 17,
 "thumbUrl": "./static/img/area/taijiang.png"
 }, {
 "id": "be702db5fdf611e7ba2d00163e0c27f8",
 "name": "劍河",
 "desc": "這是劍河喲~",
 "num": 17,
 "thumbUrl": "./static/img/area/jianhe.png"
 }
]

關(guān)于vue.js組件的教程,請(qǐng)大家點(diǎn)擊專題vue.js組件學(xué)習(xí)教程進(jìn)行學(xué)習(xí)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Pinia進(jìn)階setup函數(shù)式寫法封裝到企業(yè)項(xiàng)目

    Pinia進(jìn)階setup函數(shù)式寫法封裝到企業(yè)項(xiàng)目

    這篇文章主要為大家介紹了Pinia進(jìn)階setup函數(shù)式寫法封裝到企業(yè)項(xiàng)目實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 在vue中根據(jù)光標(biāo)的顯示與消失實(shí)現(xiàn)下拉列表

    在vue中根據(jù)光標(biāo)的顯示與消失實(shí)現(xiàn)下拉列表

    這篇文章主要介紹了在vue中根據(jù)光標(biāo)的顯示與消失實(shí)現(xiàn)下拉列表,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • vue上傳圖片到oss的方法示例(圖片帶有刪除功能)

    vue上傳圖片到oss的方法示例(圖片帶有刪除功能)

    這篇文章主要介紹了vue上傳圖片到oss的方法示例(圖片帶有刪除功能),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • 基于Vue實(shí)現(xiàn)自定義組件的方式引入圖標(biāo)

    基于Vue實(shí)現(xiàn)自定義組件的方式引入圖標(biāo)

    在vue項(xiàng)目中我們經(jīng)常遇到圖標(biāo),下面這篇文章主要給大家介紹了關(guān)于如何基于Vue實(shí)現(xiàn)自定義組件的方式引入圖標(biāo)的相關(guān)資料,文章通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-07-07
  • vue在index.html中引入靜態(tài)文件不生效問題及解決方法

    vue在index.html中引入靜態(tài)文件不生效問題及解決方法

    這篇文章主要介紹了vue在index.html中引入靜態(tài)文件不生效問題及解決方法,本文給大家分享兩種原因分析,通過實(shí)例代碼講解的非常詳細(xì) ,需要的朋友可以參考下
    2019-04-04
  • Vue3?封裝?Element?Plus?Menu?無限級(jí)菜單組件功能的詳細(xì)代碼

    Vue3?封裝?Element?Plus?Menu?無限級(jí)菜單組件功能的詳細(xì)代碼

    本文分別使用?SFC(模板方式)和?tsx?方式對(duì)?Element?Plus?*el-menu*?組件進(jìn)行二次封裝,實(shí)現(xiàn)配置化的菜單,有了配置化的菜單,后續(xù)便可以根據(jù)路由動(dòng)態(tài)渲染菜單,對(duì)Vue3?無限級(jí)菜單組件相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-09-09
  • 解決vue頁面刷新vuex中state數(shù)據(jù)丟失的問題

    解決vue頁面刷新vuex中state數(shù)據(jù)丟失的問題

    這篇文章介紹了解決vue頁面刷新vuex中state數(shù)據(jù)丟失的問題,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-01-01
  • 教你如何開發(fā)Vite3插件構(gòu)建Electron開發(fā)環(huán)境

    教你如何開發(fā)Vite3插件構(gòu)建Electron開發(fā)環(huán)境

    這篇文章主要介紹了如何開發(fā)Vite3插件構(gòu)建Electron開發(fā)環(huán)境,文中給大家提到了如何讓 Vite 加載 Electron 的內(nèi)置模塊和 Node.js 的內(nèi)置模塊,需要的朋友可以參考下
    2022-11-11
  • vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析

    vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析

    這篇文章主要介紹了vue-cli創(chuàng)建的項(xiàng)目中的gitHooks原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 一篇文章教會(huì)你部署vue項(xiàng)目到docker

    一篇文章教會(huì)你部署vue項(xiàng)目到docker

    在前端開發(fā)中,部署項(xiàng)目是我們經(jīng)常發(fā)生的事情,下面這篇文章主要給大家介紹了關(guān)于部署vue項(xiàng)目到docker的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04

最新評(píng)論