Skip to content

彆扭的滑動條 slider

很有個性的滑動條 ( ´థ౪థ)

使用範例

基本用法

可以自定義滑動邏輯

往右拉很快、往左拉很慢,滑鼠太近時握把會落跑 ԅ(´∀` ԅ)

value: 0
查看範例原始碼
vue
<template>
  <div class="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
查看範例原始碼
vue
<template>
  <div class="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 = 1000000
const data = ref(10000)
const money = computed(() => data.value.toLocaleString())

const stuckThreshold = max / 10 * 8
const 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>

原理

移動基於物理模擬

原始碼

API

Props

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);
}

Emits

const emit = defineEmits<{
  'update:modelValue': [value: Props['modelValue']];
}>()

v0.42.2