#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版)

<<:  DAY6 起手式--Nuxt.js常用套件安装

>>:  Day 5:建立专案(三):专案档案架构

玩转 Storybook: Day 29 Design System for Developers - Distribute

在整个组织中发布 Design System 从架构的角度来看,Design System 如同 l...

[Day12] JavaScript 的动态型别

我们可以在变数做宣告时,是否绑定型别来判定程序语言是动态型别或是静态型别,而两者区别如下: 动态型别...

Day18# Leetcode TwoSum

接下来的 12 天,会用每天刷 leetcode 练习的方式来练习 Go 那麽话不多说,我们就进入正...

Day24 - 遇到 404 或 500 怎麽办,客制化错误页面

前言 在「错误捕捉、全域 CSS、共用 Layout,就用 _app.tsx 来搞定吧!」这边文章中...

DAY30 - 切版的下一步

终於来到最後一天了!真的很怕最後一天出什麽意外啊~~ (突然昏睡24小时,今天就过了之类的 ? ) ...