每條游來游去的小魚,都是正在瀏覽這一頁的訪客,有一條是你自己 ( ´ ▽ ` )ノ 覺得干擾閱讀的話,可以把小魚關掉。
彆扭的滑動條
slider很有個性的滑動條 ( ´థ౪థ)
| 名稱 | 描述 |
|---|---|
| Pointer 事件 | 偵測滑鼠或觸控點移動、點擊、懸停等等事件,取得座標、目標等等資訊 |
| JS 動畫 | 基於 JavaScript 實現的動畫,達成更複雜、精準的動畫控制,常見套件有 GSAP、anime.js 等 |
| 物理模擬 | 模擬真實世界物理現象,如重力、碰撞、速度等物理效果 |
| Anime.js | 輕量級 JavaScript 動畫函式庫 |
可以自定義滑動邏輯
往右拉很快、往左拉很慢,滑鼠太近時握把會落跑 ԅ(´∀` ԅ)
value: 0
<template> <div class="mx-auto max-w-[20rem] w-full flex flex-col gap-4 py-14"> value: {{ data }}
<slider-awkward v-model="data" :physical-data="physicalDataStrategy" /> </div></template>
<script setup lang="ts">import type { ComponentProps } from 'vue-component-type-helpers'import { ref } from 'vue'import SliderAwkward from '../slider-awkward.vue'
type Props = ComponentProps<typeof SliderAwkward>
const data = ref(0)
const physicalDataStrategy: Props['physicalData'] = (params) => { const { velocity, value, targetValue, pointerPosition } = params
if (pointerPosition.x < -50) { return { acceleration: -0.0001, maxSpeed: velocity > 0 ? 0.05 : 1, } }
if (targetValue <= value) { return { acceleration: 0.001, maxSpeed: 5, } }
const sign = Math.sign(targetValue - value)
return { velocity: sign * 9999, }}</script>讓客人好好思考思考 („ಡωಡ )
$10,000
<template> <div class="mx-auto max-w-[20rem] w-full flex flex-col gap-4 py-14"> ${{ money }}
<slider-awkward v-model="data" :physical-data="physicalDataStrategy" :min="1000" :step="100" :max /> </div></template>
<script setup lang="ts">import type { ComponentProps } from 'vue-component-type-helpers'import { computed, ref } from 'vue'import SliderAwkward from '../slider-awkward.vue'
type Props = ComponentProps<typeof SliderAwkward>
const max = 1000000const data = ref(10000)const money = computed(() => data.value.toLocaleString())
const stuckThreshold = max / 10 * 8const physicalDataStrategy: Props['physicalData'] = (params) => { const { value, targetValue } = params
if (value > stuckThreshold) { return { stuckAt: max, escapeValue: max / 3 * 2, } }
if (targetValue > value) { return { velocity: 100, } }
return { acceleration: -0.0001, maxSpeed: 0.01, }}</script>移動基於物理模擬
export type PhysicalData = { /** px/ms */ velocity: number;} | { /** px/ms^2 */ acceleration: number; /** 可限制最大速度,僅為正數 */ maxSpeed?: number;} | { /** 卡在指定數值,限制於 min、max 之間 */ stuckAt: number; /** 滑鼠拉動目標數值之差大於此值即可掙脫,掙脫後 thumb 會移動至此 */ escapeValue: number;}
export interface Props { modelValue: number; disabled?: boolean; min?: number; max?: number; step?: number; thumbSize?: number; thumbColor?: string; trackClass?: string;
/** 物理參數,可以控制移動方式 * * 如果是函式,則會在每次動畫更新時呼叫 */ physicalData?: PhysicalData | ((data: { /** 目前移動速度 */ velocity: number; /** 目前數值 */ value: number; /** 目標數值 */ targetValue: number; /** 目前比例 */ ratio: number; /** 滑鼠在 slider 上的比例 */ pointerRatio: number; /** 以 thumb 中心點為 (0, 0) */ pointerPosition: { x: number; y: number }; }) => PhysicalData);}const emit = defineEmits<{ 'update:modelValue': [value: Props['modelValue']];}>()