每條游來游去的小魚,都是正在瀏覽這一頁的訪客,有一條是你自己 ( ´ ▽ ` )ノ 覺得干擾閱讀的話,可以把小魚關掉。
脆弱的滑動條
slider會斷掉 (´・ω・`)
| 名稱 | 描述 |
|---|---|
| Anime.js | 輕量級 JavaScript 動畫函式庫 |
| Pointer 事件 | 偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊 |
| 物理模擬 | 模擬真實世界物理現象,如重力、碰撞、速度等物理效果 |
| CSS 動畫 | 基於 CSS transition 和 animation 實現 |
| JS 動畫 | 基於 JavaScript 實現的動畫,達成更複雜、精準的動畫控制,常見套件有 GSAP、anime.js 等 |
拉太偏會讓握把斷掉 (´・ω・`)
value: 50
<template> <div class="mx-auto max-w-[20rem] w-full flex flex-col gap-4 py-14"> value: {{ Math.floor(value) }}
<slider-stubborn v-model="value" class="w-full" /> </div></template>
<script setup lang="ts">import { ref } from 'vue'import SliderStubborn from '../slider-fragile.vue'
const value = ref(50)</script>可以主動修復或斷開握把 (´・ω・`)ノ
<template> <div class="mx-auto max-w-[20rem] w-full flex flex-col gap-4"> <div class="w-full py-12"> <slider-stubborn ref="slider" v-model="value" class="w-full" durable thumb-color="#34c600" joint-color="#a0d48e" /> </div>
<div class="flex gap-4"> <base-btn :label="t('repair')" class="flex-1" @click="sliderRef?.repair()" /> <base-btn :label="t('break')" class="flex-1" @click="sliderRef?.break()" /> </div> </div></template>
<script setup lang="ts">import { ref, useTemplateRef } from 'vue'import { useI18n } from 'vue-i18n'import BaseBtn from '../../base-btn.vue'import SliderStubborn from '../slider-fragile.vue'
const sliderRef = useTemplateRef('slider')const value = ref(50)
const { t } = useI18n()</script>寧死不屈的甜度 ◝(´・ω・`)◟
飲料類型:
甜度:10 分
冰量:0 分
<template> <div class="mx-auto max-w-[28rem] w-full flex flex-col gap-6 py-20"> <div class="example-wrap flex flex-col items-start gap-4"> <div class="font-bold"> {{ t('drinkType') }} </div>
<div class="w-full flex flex-wrap justify-between gap-4 whitespace-nowrap"> <label v-for="item, i in list" :key="i" > <input v-model="value" type="radio" :value="item" > {{ t(`drinkOptions.${item}`) }} </label> </div> </div>
<div class="grid grid-cols-6 w-full flex-nowrap items-center gap-8"> <div class="col-span-2 text-lg"> {{ t('sweetness') }}:10 {{ t('points') }} </div>
<div class="col-span-4 pr-4"> <slider-fragile ref="slider" :model-value="10" disabled :max="10" /> </div> </div>
<div class="grid grid-cols-6 w-full flex-nowrap items-center gap-8"> <div class="col-span-2 text-lg"> {{ t('ice') }}:{{ iceValue }} {{ t('points') }} </div>
<div class="col-span-4 pr-4"> <slider-fragile v-model="iceValue" durable :step="3" :max="9" /> </div> </div> </div></template>
<script setup lang="ts">import { ref, useTemplateRef, watch } from 'vue'import { useI18n } from 'vue-i18n'import SliderFragile from '../slider-fragile.vue'
const sliderRef = useTemplateRef('slider')
const { t } = useI18n()
const value = ref('綠茶')const list = [ '綠茶', '紅茶', '蜂蜜奶茶', '鮮奶茶', '泰奶', '水果茶', '檸檬茶',]
const iceValue = ref(0)
watch(value, () => { sliderRef.value?.repair()})</script>過低時斷給你看,具現化何謂一分錢一分貨 (◞‸◟ )
預算:$5000
<template> <div class="mx-auto max-w-[20rem] w-full flex flex-col gap-6 py-[20vh]"> <div class="w-full flex flex-col flex-nowrap gap-8"> <div class="flex-1 text-lg font-bold"> {{ t('budget') }}${{ data }} </div>
<div class="flex-1"> <slider-fragile ref="slider" v-model="data" durable :max="10000" /> </div> </div> </div></template>
<script setup lang="ts">import { ref, useTemplateRef, watch } from 'vue'import { useI18n } from 'vue-i18n'import SliderFragile from '../slider-fragile.vue'
const sliderRef = useTemplateRef('slider')
const { t } = useI18n()
const data = ref(5000)
watch(data, (value) => { if (value < 1000) { sliderRef.value?.break() } else { sliderRef.value?.repair() }})</script>主要由兩個元件組成:
主體(slider-fragile.vue)
Section titled “主體(slider-fragile.vue)”slider-fragile.vue 負責處理滑動條的基本邏輯,像是計算數值、偵測滑鼠是否在範圍內。
它會將計算好的比例 (ratio)、滑鼠互動狀態 (isHeld) 等資訊傳遞給握把元件 slider-fragile-thumb.vue。
握把 (slider-fragile-thumb.vue)
Section titled “握把 (slider-fragile-thumb.vue)”斷裂與移動效果皆由 slider-fragile-thumb.vue 負責。
當使用者按住握把 (isHeld 為 true) 並拖曳時,計算滑鼠指標相對於握把中心的距離。
距離越大,震動效果越大,模擬被用力拉扯的感覺。
有以下狀態:
-
正常狀態: 握把根據父元件傳來的
ratio定位在滑動條上。 -
斷裂: 若拖曳的距離超過設定的
breakLength閾值且時間超過minSecondsToBreak,isBroken會變成true,表示握把斷裂。 -
斷裂後:
- 放開滑鼠時,執行物理模擬,給予握把一個初始速度 (依放開前滑鼠的移動方向),然後模擬重力、空氣阻力,並在碰到視窗邊界時反彈,直到速度減緩至停止 (
thumbData.sleep)。 - 斷裂後如果再次按住握把,它會跟隨滑鼠移動。
- 放開滑鼠時,執行物理模擬,給予握把一個初始速度 (依放開前滑鼠的移動方向),然後模擬重力、空氣阻力,並在碰到視窗邊界時反彈,直到速度減緩至停止 (
比較特別的部分是正常狀態與斷裂狀態定位方式不同,正常狀態是 absolute 定位,斷裂狀態是 fixed 定位。
interface Props { modelValue: number; disabled?: boolean; min?: number; max?: number; step?: number; thumbSize?: number; thumbColor?: string; jointColor?: string; trackClass?: string; durable?: boolean; breakLength?: number; minSecondsToBreak?: number;}const emit = defineEmits<{ 'update:modelValue': [value: Props['modelValue']];}>()Methods
Section titled “Methods”interface Expose { repair: () => void; break: () => void;}