Codepen Link: https://codepen.io/zyrxdkoz/pen/ZEyBeJw
心得
*原生JS版請參考前面文章
<template>
<home-btn />
<section>
<div
class="
bg
absolute
top-[-3%]
left-[-3%]
min-h-[106%] min-w-[1024px]
w-[106%]
h-auto
z-[-1]
"
// 將style屬性透過Computed來綁定
:style="bgOpacity"
></div>
<div class="text-position text-5xl text-white" :style="textOpacity">
{{ loadNum }}%
</div>
</section>
</template>
<script>
import homeBtn from '../components/HomeBtn.vue'
import { computed, ref } from 'vue'
export default {
name: '#1. BlurringImageLoading',
components: {
homeBtn,
},
setup() {
let loadNum = ref(1)
const scale = (num, in_min, in_max, out_min, out_max) => {
return (
((num - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min
)
}
const bgOpacity = computed(() => {
return {
filter: `blur(${scale(loadNum.value, 0, 100, 30, 0)}px)`,
}
})
const textOpacity = computed(() => {
return {
opacity: scale(loadNum.value, 0, 100, 1, 0),
}
})
const blurring = () => {
loadNum.value++
if (loadNum.value > 99) {
clearInterval(timer)
}
}
// 宣告timer就會直接運作,不需要寫成函式
const timer = setInterval(blurring, 30)
return {
timer,
loadNum,
blurring,
scale,
bgOpacity,
textOpacity,
}
}
}
</script>
<style lang="scss" scoped>
.bg {
background: url('https://source.unsplash.com/1600x900/?nature,mountain') no-repeat center center/cover;
}
.text-position {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
心得
*原生JS版請參考前面文章
Demo Link
<template>
<home-btn />
<div
class="
container
flex w-[100vw] h-[100vh]
justify-center items-center
overflow-hidden
mt-5 md:m-10"
>
// 用v-for將資料渲染成一個個div,
// 帶入固定class、動態class、點擊調用被綁定的函式、用id定義div的key。
<div
v-for="(panel) in panels"
class="panel"
:class="{ active: panel.isActive }"
@click="toggleValid(panel.id)"
:key="panel.id"
>
<img :src="panel.url" class="h-[70vh] rounded-3xl object-cover" />
<h3 class="text-gray-800">{{ panel.title }}</h3>
</div>
</div>
</template>
<script>
import homeBtn from '../components/HomeBtn.vue'
import { computed, ref } from 'vue'
export default {
name: '#2. ExpandingCards',
components: {
homeBtn,
},
setup() {
// 建立待渲染的資料
const panelArray = [
{
url: 'https://unsplash.com/photos/J4yQp1lIJsQ/download?force=true&w=1920',
title: '2020 Tokyo Olympic'
isActive: false,
id:1
},
{
url: 'https://unsplash.com/photos/3R4vPrSB1c4/download?force=true&w=1920',
title: 'Running',
isActive: false,
id:2
},
{
url: 'https://unsplash.com/photos/hawN8XnaJuY/download?force=true&w=1920',
title: 'Swimming',
isActive: false,
id:3
},
{
url: 'https://unsplash.com/photos/NCwfsHQvhy0/download?force=true&w=1920',
title: 'Baseball',
isActive: false,
id:4
},
{
url: 'https://unsplash.com/photos/Yf1SegAI84o/download?force=true&w=1920',
title: 'Martial Art',
isActive: false,
id:5
},
]
// 用ref包裝起來,然後return出去
const panels = ref(panelArray)
// 當收到點擊事件時,帶入id,比對panels.id來判斷,然後做相對應的行為
const toggleValid = (id) => {
panels.value.forEach(function(panel){
if (id === panel.id) {
panel.isActive = !panel.isActive
} else {
panel.isActive = false
}
})
}
return {
panels,
toggleValid,
}
}
}
</script>
<style lang="scss" scoped>
@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
div {
font-family: 'Roboto', sans-serif;
}
.container {
.panel {
height: 80vh;
color: white;
cursor: pointer;
// 無單位數字:flex-grow,分配剩餘空間
flex: 0.5;
margin: 10px;
position: relative;
transition: flex 0.5s ease-in;
h3 {
font-size: 24px;
position: absolute;
bottom: 7%;
left: 1%;
opacity: 0;
}
}
.active {
flex: 5;
img {
width: 100%;
object-fit: cover;
}
h3 {
opacity: 1;
transition: opacity 0.3s ease-in 0.4s;
}
}
}
@media screen and (max-width: 560px) {
.container :nth-child(4) {
display: none;
}
}
</style>
心得