Taro+vue3?實現(xiàn)電影切換列表功能
1.需求
我們在做類似于貓眼電影的小程序或者H5 的時候 我們會做到那種 左右滑動的電影列表,這種列表一般帶有電影場次
2.效果

3.說明
這種效果在淘票票 貓眼電影上 都有的 ,一般電影類型的H5 或者小程序 這個是都有的 第一是好看 第二是客觀性比較好
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="暫無場次"></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)爭',
cast: '吳京 易烊千璽 段奕宏 張涵予 朱亞文',
posterUrl: "https://gw.alicdn.com/i1/O1CN01sSmj2b1daSm6IAUcs_!!6000000003752-0-alipicbeacon.jpg_480x480Q30s150.jpg",
movieName: '長津湖之水門橋',
})
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)爭',
cast: '吳京 易烊千璽 段奕宏 張涵予 朱亞文',
movieName: '長津湖之水門橋',
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: "三大隊",
movieType: "劇情,犯罪",
posterUrl: "https://gw.alicdn.com/i4/O1CN01zQvWon1SuCCkUXTr8_!!6000000002306-0-alipicbeacon.jpg_480x480Q30s150.jpg"
},
{
cast: "岑珈其,梁朝偉,劉德華,蔡卓妍,任達華,陳家樂,白只,姜皓文,方中信,太保,錢嘉樂,周家怡",
duration: 106,
movieId: 147887,
movieName: "金手指",
movieType: "犯罪,劇情",
posterUrl: "https://gw.alicdn.com/i4/O1CN01mdCwok1K4QV7FV8gv_!!6000000001110-0-alipicbeacon.jpg_480x480Q30s150.jpg"
},
{
cast: "李棟,大鵬,白客,莊達菲,王迅,孫藝洲,李乃文",
duration: 106,
movieId: 147888,
movieName: "年會不能停!",
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: "動畫,懸疑,動作",
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號激光杜比全景聲廳",
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);
};
//計算電影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 移動的距離是
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">萬達影城(北京昌平保利光魔店)</div>
<div class="address">昌平區(qū)鼓樓街賈璉時代廣場四樓</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.我這個項目是基于Taro +vue3 +ts 來寫的 用的組件庫也是京東的nut-ui 以上的代碼和組件 也有的是我二次封裝的組件 組件也挺方便的
到此這篇關(guān)于Taro+vue3 實現(xiàn)電影切換列表的文章就介紹到這了,更多相關(guān)vue3電影切換列表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue 動態(tài)改變靜態(tài)圖片以及請求網(wǎng)絡(luò)圖片的實現(xiàn)方法
下面小編就為大家分享一篇vue 動態(tài)改變靜態(tài)圖片以及請求網(wǎng)絡(luò)圖片的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02
vue防止花括號{{}}閃爍v-text和v-html、v-cloak用法示例
這篇文章主要介紹了vue防止花括號{{}}閃爍v-text和v-html、v-cloak用法,結(jié)合實例形式分析了vue.js使用v-text和v-html、v-cloak防止花括號{{}}閃爍的解決方法,需要的朋友可以參考下2019-03-03
vue.js 實現(xiàn)a標(biāo)簽href里添加參數(shù)
今天小編就為大家分享一篇vue.js 實現(xiàn)a標(biāo)簽href里添加參數(shù),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11
在Vue中延遲執(zhí)行某個函數(shù)的實現(xiàn)方式
在Vue中延遲執(zhí)行某個函數(shù),你可以使用setTimeout()函數(shù)或者Vue提供的生命周期鉤子函數(shù),本文通過一些示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-12-12
Vue3如何理解ref toRef和toRefs的區(qū)別
本文主要介紹了Vue3如何理解ref toRef和toRefs的區(qū)別,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12
關(guān)于Vue.js一些問題和思考學(xué)習(xí)筆記(2)
這篇文章主要為大家分享了關(guān)于Vue.js一些問題和思考的學(xué)習(xí)筆記,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12

