Taro+vue3?實(shí)現(xiàn)電影切換列表功能
1.需求
我們?cè)谧鲱愃朴谪堁垭娪暗男〕绦蚧蛘逪5 的時(shí)候 我們會(huì)做到那種 左右滑動(dòng)的電影列表,這種列表一般帶有電影場(chǎng)次
2.效果
3.說明
這種效果在淘票票 貓眼電影上 都有的 ,一般電影類型的H5 或者小程序 這個(gè)是都有的 第一是好看 第二是客觀性比較好
4.代碼
1.整體頁面 <template> <div class="movie-container-index"> <Header></Header> <div class="swiper-main"> <image class="background-img-vague" :src="chooseMovice.posterUrl" style="height: 100%;"></image> <div class="wrap"> <MovieList :list="movieList" @onchangeMovie="MovieChange" :model-value="chooseMovice.movieId"></MovieList> </div> <div class="box"></div> </div> <div class="movie-detail"> <div class="name">{{ chooseMovice.movieName }}</div> <div class="center-detail"> <div>{{ chooseMovice.movieType }}</div> <div>{{ chooseMovice.duration }}</div> </div> <div class="cast">{{ chooseMovice.cast }}</div> </div> <div class="movie-time-container"> <Filter v-if="timeList.length" :data="timeList" @onChanged="onTimeChanged"></Filter> </div> <div class="movie-arrgement-container" v-if="arrangementList.length && !loading"> <Item v-for="(item, index) in arrangementList" :info="item" :key="index"></Item> </div> <template v-if="arrangementList.length === 0 && loading"> <div style="padding: 0 15px;"> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> <nut-skeleton :style="{ width: '100%', margin: '0.5rem 0' }" width="100%" height="15px" title animated row="1"> </nut-skeleton> </div> </template> <template v-if="arrangementList.length === 0 && !loading"> <nut-empty description="暫無場(chǎng)次"></nut-empty> </template> </div> </template> <script setup> import Header from './header.vue' import MovieList from './movie-list.vue' import Filter from './filter.vue' import { ref, reactive, onMounted } from 'vue' import Item from './item.vue' const chooseMovice = ref({ movieId: 12343, duration: 149, movieType: '劇情|歷史|戰(zhàn)爭(zhēng)', cast: '吳京 易烊千璽 段奕宏 張涵予 朱亞文', posterUrl: "https://gw.alicdn.com/i1/O1CN01sSmj2b1daSm6IAUcs_!!6000000003752-0-alipicbeacon.jpg_480x480Q30s150.jpg", movieName: '長(zhǎng)津湖之水門橋', }) const timeList = ref(["2024-01-09", "2024-01-10", "2024-01-13"]) const arrangementList = ref([]) const loading = ref(true) const movieList = ref([ { movieId: 12343, duration: 149, movieType: '劇情|歷史|戰(zhàn)爭(zhēng)', cast: '吳京 易烊千璽 段奕宏 張涵予 朱亞文', movieName: '長(zhǎng)津湖之水門橋', posterUrl: "https://gw.alicdn.com/i1/O1CN01sSmj2b1daSm6IAUcs_!!6000000003752-0-alipicbeacon.jpg_480x480Q30s150.jpg" }, { cast: "易烊千璽 田雨 陳哈琳 齊溪 公磊 許君聰 王寧 黃堯 鞏金國", duration: 106, movieId: 147885, movieName: "奇跡·笨小孩", movieType: "劇情", posterUrl: "https://gw.alicdn.com/i1/O1CN013Ggc2s1Z8HwrwxAfn_!!6000000003149-0-alipicbeacon.jpg_480x480Q30s150.jpg" }, { cast: "張譯,李晨,魏晨,曹炳琨,王驍,張子賢,楊新鳴", duration: 106, movieId: 147886, movieName: "三大隊(duì)", movieType: "劇情,犯罪", posterUrl: "https://gw.alicdn.com/i4/O1CN01zQvWon1SuCCkUXTr8_!!6000000002306-0-alipicbeacon.jpg_480x480Q30s150.jpg" }, { cast: "岑珈其,梁朝偉,劉德華,蔡卓妍,任達(dá)華,陳家樂,白只,姜皓文,方中信,太保,錢嘉樂,周家怡", duration: 106, movieId: 147887, movieName: "金手指", movieType: "犯罪,劇情", posterUrl: "https://gw.alicdn.com/i4/O1CN01mdCwok1K4QV7FV8gv_!!6000000001110-0-alipicbeacon.jpg_480x480Q30s150.jpg" }, { cast: "李棟,大鵬,白客,莊達(dá)菲,王迅,孫藝洲,李乃文", duration: 106, movieId: 147888, movieName: "年會(huì)不能停!", movieType: "喜劇,劇情", posterUrl: "https://gw.alicdn.com/i1/O1CN01v6g8341QMBaLa2FhI_!!6000000001961-0-alipicbeacon.jpg_480x480Q30s150.jpg" }, { cast: "屈楚蕭,張佳寧,傅菁,蔣昀霖,牛超,田壯壯,沙溢", duration: 106, movieId: 147889, movieName: "一閃一閃亮星星", movieType: "愛情,奇幻", posterUrl: "https://gw.alicdn.com/i4/O1CN01W8mzt61aa3k1Xtw3N_!!6000000003345-0-alipicbeacon.jpg_480x480Q30s150.jpg" }, { cast: "江戶川柯南,高山南,山崎和佳奈,小山力也,林原惠美,置鲇龍?zhí)?三石琴乃,土師孝也,乃村健次,飛田展男,緒方賢一,松井菜櫻子,巖居由希子,大谷育江,茶風(fēng)林", duration: 106, movieId: 147890, movieName: "名偵探柯南:黑鐵的魚影", movieType: "動(dòng)畫,懸疑,動(dòng)作", posterUrl: "https://gw.alicdn.com/i1/O1CN01U9wWR81pfku0aqB6O_!!6000000005388-0-alipicbeacon.jpg_480x480Q30s150.jpg" } ]) const MovieChange = (e) => { console.log(e); chooseMovice.value = movieList.value.filter((item) => item.movieId == e)[0] } onMounted(() => { arrangementList.value = [ { finishTime: "1704782520000", hallName: "7號(hào)激光杜比全景聲廳", id: "1194219979173253121", pickUpPrice: 3380, price: 3500, showTime: "1704775500000" } ] loading.value = false }) </script> <style lang="scss"> .movie-container-index { display: flex; flex-direction: column; .movie-arregment-container {} .swiper-main { height: 324px; position: relative; overflow: hidden; width: 100%; .background-img-vague { position: absolute; left: 0; right: 0; width: 100%; height: 100%; filter: blur(15px); -webkit-filter: blur(15px); } } .movie-detail { display: flex; flex-direction: column; align-items: center; justify-content: center; background-color: #fff; padding: 20px; .name { font-size: 30px; font-weight: 700; color: #15181d; } .center-detail { display: flex; align-items: center; color: #858a99; font-size: 24px; margin-top: 5px; } .cast { color: #858a99; font-size: 24px; margin-top: 5px; word-break: break-all; text-align: center; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } } .wrap { padding: 10px 0; } .box { width: 0; height: 0; border: 10rpx solid; border-color: transparent transparent #fff #fff; transform: rotate(135deg); position: absolute; bottom: -10rpx; left: 0; right: 0; margin: 0 auto; } } </style>
2.movie-list代碼 <template> <div class="movie-list-component"> <div class="list-container"> <scroll-view id="contentScroll" class="scroll-view" :scroll-with-animation="true" :scroll-left="scrollLeft" :scroll-x="true"> <div class="movie-item seat"></div> <div :data-id="item.movieId" @click="selectMovie" :id="`movieItem${item.movieId}`" class="movie-item" :class="{ active: modelValue == item.movieId }" v-for="( item, index ) in list " :key="index"> <div class="img-container"> <image class="img" :src="item?.posterUrl" alt="" /> </div> </div> <div class="movie-item seat1"></div> </scroll-view> </div> </div> </template> <script setup lang="ts"> import Taro from "@tarojs/taro"; import { onMounted, ref, reactive, toRefs, watch } from "vue"; const props = defineProps({ // 子組件接收父組件傳遞過來的值 list: { type: Array<any>, required: true, }, modelValue: { type: Number, required: true, }, }); //使用父組件傳遞過來的值 const { list, modelValue } = toRefs(props); const emit = defineEmits(["onchangeMovie"]); onMounted(() => { // selectMovie(list.value[0].movieId) list?.value.map((item, index) => { if (item.id === modelValue?.value) { list?.value.unshift(list?.value.splice(index, 1)[0]); } }); }); const scrollLeft = ref(0); //選擇電影 const selectMovie = (e) => { let offsetLeft = e.currentTarget.offsetLeft; let { id } = e.currentTarget.dataset; if (!id) { id = list.value[0].movieId; } if (modelValue.value == id) { return; } emit("onchangeMovie", id); getRect(id, offsetLeft); }; //計(jì)算電影item的偏移量 const getRect = async (id, offsetLeft) => { const eleId = `#movieItem${id}`; const contentScrollWidth: any = await getContentScrollWidth("#contentScroll"); const query = Taro.createSelectorQuery(); query.select(eleId).boundingClientRect(); query.selectViewport().scrollOffset(); query.exec(async (res) => { //獲取item的寬度de 一半 const subhalfwidth = res[0].width / 2; //需要scrollview 移動(dòng)的距離是 const juli = offsetLeft - contentScrollWidth / 2 + subhalfwidth; scrollLeft.value = juli; }); }; // 獲取ScrollView的寬度 const getContentScrollWidth = (ele) => { return new Promise((resolve) => { const query = Taro.createSelectorQuery(); query.select(ele).boundingClientRect(); query.selectViewport().scrollOffset(); query.exec((res) => { const width = res[0].width; resolve(width); }); }); }; </script> <style lang="scss"> .movie-list-component { display: flex; flex-direction: column; .list-container { display: flex; flex-direction: column; justify-content: center; height: 324px; .scroll-view { width: 100%; height: 324px; white-space: nowrap; position: relative; .movie-item:nth-child(n+2) { margin-left: 35px; } .movie-item { display: inline-block; position: relative; margin-top: 30px; border-radius: 18px; width: 156px; height: 218px; // line-height: 208px; transition: width 1s; transition: height 1s; .img-container { border-radius: 8px; width: 100%; height: 100%; overflow: hidden; position: relative; z-index: 2; // border: 5px #ffffff solid; .img { width: 100%; height: 100%; } } } .movie-item.active { transform: scale(1.15); /* 放大1.2倍 */ transition: transform 1s ease; /* 過渡效果 */ } .seat { display: inline-block; width: 50%; // height: 290px; margin-left: -110px; } .seat1 { display: inline-block; width: 35%; } } } } </style>
filter 組件 <template> <div class="cinema-detail-filter-container"> <div class="date-tab"> <nut-tabs :title-gutter="15" v-model="tabTimesIndex" title-scroll> <nut-tab-pane v-for="(item, index) in tabTimesList" :pane-key="index" :title="`${item.t} ${item.a}`"> </nut-tab-pane> </nut-tabs> </div> </div> </template> <script setup lang="ts"> import { computed, onMounted, ref, toRefs, watch } from 'vue'; import moment from "moment"; const tabTimesIndex = ref(0) const tabTimesList: any = ref([]) const props = defineProps({ //子組件接收父組件傳遞過來的值 data: Object, }); //使用父組件傳遞過來的值 const { data: timesList } = toRefs(props); onMounted(() => { getTimes() }); const emit = defineEmits(['onChanged']) watch(tabTimesIndex, (index) => { emit('onChanged', tabTimesList.value[index]) }) watch(tabTimesList, () => { emit('onChanged', tabTimesList.value[0]) }) const getTimes = () => { const times = timesList?.value const arr: any = [] times?.forEach(item => { const time = item let t = getweek(moment(time).startOf('day').format('E')) if (time === moment().format('YYYY-MM-DD')) { t = '今天' } else if (time === moment().subtract(-1, 'days').format('YYYY-MM-DD')) { t = '明天' } else if (time === moment().subtract(-2, 'days').format('YYYY-MM-DD')) { t = '后天' } arr.push({ time: time, t: t, a: time.substr(5, 9) }) }); tabTimesList.value = arr } const getweek = (val) => { const week = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] return week[val - 1]; }; </script> <style lang="scss"> .cinema-detail-filter-container { display: flex; flex-direction: column; .nut-tabs { .nut-tabs__titles { background: #ffffff !important; .nut-tabs__titles-item { .nut-tabs__titles-item__smile { display: none; } .nut-tabs__titles-item__text { color: #858a99; font-size: 22px !important; } } .nut-tabs__titles-item__line { background: linear-gradient(to right, #5232B7, #7237BC, #5232B7) !important; border-radius: 30px !important; } .nut-tabs__titles-item.active { .nut-tabs__titles-item__smile { display: block; margin-top: 10px; } .nut-tabs__titles-item__text { color: #15181d; } } } .nut-tabs__content { display: none !important; } } } </style>
header 組件 <template> <div class="movie-header-box"> <div class="left"> <div class="name">萬達(dá)影城(北京昌平保利光魔店)</div> <div class="address">昌平區(qū)鼓樓街賈璉時(shí)代廣場(chǎng)四樓</div> </div> <div class="right"> <IconFont name="locationg3" color="#5232B7"></IconFont> </div> </div> </template> <script setup> import { IconFont } from '@nutui/icons-vue-taro'; </script> <style lang="scss"> .movie-header-box { display: flex; align-items: center; justify-content: space-between; background-color: #fff; padding: 25px 30px; .left { .name { color: #15181d; font-weight: 700; font-size: 26px; } .address { color: #858a99; font-size: 22px; margin-top: 10px; } } } </style>
5.我這個(gè)項(xiàng)目是基于Taro +vue3 +ts 來寫的 用的組件庫也是京東的nut-ui 以上的代碼和組件 也有的是我二次封裝的組件 組件也挺方便的
到此這篇關(guān)于Taro+vue3 實(shí)現(xiàn)電影切換列表的文章就介紹到這了,更多相關(guān)vue3電影切換列表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue 動(dòng)態(tài)改變靜態(tài)圖片以及請(qǐng)求網(wǎng)絡(luò)圖片的實(shí)現(xiàn)方法
下面小編就為大家分享一篇vue 動(dòng)態(tài)改變靜態(tài)圖片以及請(qǐng)求網(wǎng)絡(luò)圖片的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02vue防止花括號(hào){{}}閃爍v-text和v-html、v-cloak用法示例
這篇文章主要介紹了vue防止花括號(hào){{}}閃爍v-text和v-html、v-cloak用法,結(jié)合實(shí)例形式分析了vue.js使用v-text和v-html、v-cloak防止花括號(hào){{}}閃爍的解決方法,需要的朋友可以參考下2019-03-03基于Vue3實(shí)現(xiàn)百度地圖位置選擇器組件
在開發(fā)前端應(yīng)用時(shí),地圖選擇器是一個(gè)非常常見的需求,本文將一步步展示如何使用?Vue?3?和?Element?Plus?來實(shí)現(xiàn)一個(gè)百度地圖位置選擇器組件,有需要的可以參考一下2025-04-04使用vue開發(fā)移動(dòng)端管理后臺(tái)的注意事項(xiàng)
這篇文章主要介紹了使用vue開發(fā)移動(dòng)端管理后臺(tái)的注意事項(xiàng),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03vue.js 實(shí)現(xiàn)a標(biāo)簽href里添加參數(shù)
今天小編就為大家分享一篇vue.js 實(shí)現(xiàn)a標(biāo)簽href里添加參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-11-11在Vue中延遲執(zhí)行某個(gè)函數(shù)的實(shí)現(xiàn)方式
在Vue中延遲執(zhí)行某個(gè)函數(shù),你可以使用setTimeout()函數(shù)或者Vue提供的生命周期鉤子函數(shù),本文通過一些示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-12-12Vue3如何理解ref toRef和toRefs的區(qū)別
本文主要介紹了Vue3如何理解ref toRef和toRefs的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12關(guān)于Vue.js一些問題和思考學(xué)習(xí)筆記(2)
這篇文章主要為大家分享了關(guān)于Vue.js一些問題和思考的學(xué)習(xí)筆記,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12