iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Modern Web

30天30個前端任務系列 第 6

#5. Q&A Section(原生JS版)、#2. Blurring Loading(Vue版)、#3. Expanding Cards(Vue版)

今日任務

1. Q&A Section (原生JS寫法)

Codepen Link: https://codepen.io/zyrxdkoz/pen/ZEyBeJw

心得

  1. 因為後面兩個component改寫成Vue版花了蠻多時間,今天的就選簡單一點的...。
  2. 比較麻煩的是SCSS部分,JS算蠻單純的。明天會試著用tailwind css改寫看看

2. 照片模糊載入效果(Vue版)

*原生JS版請參考前面文章

Demo Link

<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>

心得

  1. 透過函式ref包裝後的資料型態,在使用時很容易忘記要加上.value(因為包裝後已經成為物件),但return出去到模板時就不用。這要花時間習慣一下。
  2. 手機跑起來的讀秒速度會變慢。之後會找時間研究一下。
  3. 照片部分是使用unsplash的API,重新整理畫面會隨機跳出不同照片。不過初次載入的時候會有點lag。未來更新會再加入await/async確保照片收到後才啟動模糊載入效果。

2. 卡片擴展效果(Vue版)

*原生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>

心得

  1. 用原生JS寫起來很簡單,用Vue來思考相對需要花些時間,但覺得v-for處理蠻好的。
  2. 用flex-grow、flex-shrink來玩欄位變化蠻有趣的!
  3. tailwind css在這個案例中比較難發揮,因為v-for部分已經用了很多屬性,再加上一堆tailwind css屬性會顯得很難閱讀,最終還是用一個語意化css(.panel)來切版。結論還是一樣:tailwind css和SCSS各有方便的地方。

明日任務:

  1. Scroll Animation 卡片滑動載入效果(原生JS版)
  2. Progress Steps 進度條按鈕(原生JS版)

上一篇
#4. Covid 19 Tracker(Vue版)
下一篇
#6. Scroll Animation(原生JS版), #7. Progress Steps(原生JS版)
系列文
30天30個前端任務19

尚未有邦友留言

立即登入留言