ToggleSwitch.vue 5.58 KB
<script lang="ts">
  export default {
    inheritAttrs: false,
  };
</script>
<script lang="ts" setup>
  import { computed } from '@vue/reactivity';
  import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util';
  import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config';
  import { useSendCommand } from './useSendCommand';

  const props = defineProps<{
    value?: ControlComponentValue;
    layout?: Recordable;
    radio?: RadioRecord;
  }>();

  const emit = defineEmits(['update:value', 'change']);

  const { sendCommand } = useSendCommand();
  const handleChange = (event: Event) => {
    const _value = (event.target as HTMLInputElement).checked;
    emit('update:value', _value);
    emit('change', _value);
    sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value);
  };

  const getRadio = computed(() => {
    return props.radio! || DEFAULT_RADIO_RECORD;
  });
</script>

<template>
  <div class="flex flex-col">
    <div
      class="toggle-switch"
      :style="{
        width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }),
        height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }),
      }"
    >
      <label class="switch">
        <input
          :value="props.value?.value"
          type="checkbox"
          :checked="props.value?.value"
          @change="handleChange"
        />
        <div class="button">
          <div class="light"></div>
          <div class="dots"></div>
          <div class="characters"></div>
          <div class="shine"></div>
          <div class="shadow"></div>
        </div>
      </label>
    </div>
    <div
      class="text-center mt-2 text-gray-700"
      :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }"
    >
      {{ props.value?.attributeRename || props.value?.attribute }}</div
    >
  </div>
</template>

<style scoped>
  .toggle-switch {
    /* flex: 1 1 auto; */
    max-width: 75px;

    /* height: 97.5px; */
    display: flex;
  }

  .switch {
    background-color: black;
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0 1px 2px black, inset 0 2px 2px -2px white,
      inset 0 0 2px 15px #47434c, inset 0 0 2px 22px black;
    border-radius: 5px;
    padding: 10px;
    perspective: 700px;
  }

  .switch input {
    display: none;
  }

  .switch input:checked + .button {
    transform: translateZ(20px) rotateX(25deg);
    box-shadow: 0 -5px 10px #ff1818;
  }

  .switch input:checked + .button .light {
    animation: flicker 0.2s infinite 0.3s;
  }

  .switch input:checked + .button .shine {
    opacity: 1;
  }

  .switch input:checked + .button .shadow {
    opacity: 0;
  }

  .switch .button {
    display: flex;
    justify-content: center;
    align-items: center;
    transition: all 0.3s cubic-bezier(1, 0, 1, 1);
    transform-origin: center center -20px;
    transform: translateZ(20px) rotateX(-25deg);
    transform-style: preserve-3d;
    width: 100%;
    height: 100%;
    position: relative;
    cursor: pointer;
    background: linear-gradient(#980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
    background-color: #9b0621;
    background-repeat: no-repeat;
  }

  .switch .button::before {
    content: '';
    background: linear-gradient(
          rgba(255, 255, 255, 0.8) 10%,
          rgba(255, 255, 255, 0.3) 30%,
          #650000 75%,
          #320000
        )
        50% 50%/97% 97%,
      #b10000;
    background-repeat: no-repeat;
    width: 100%;
    height: 30px;
    transform-origin: top;
    transform: rotateX(-90deg);
    position: absolute;
    top: 0;
  }

  .switch .button::after {
    content: '';
    background-image: linear-gradient(#650000, #320000);
    width: 100%;
    height: 30px;
    transform-origin: top;
    transform: translateY(30px) rotateX(-90deg);
    position: absolute;
    bottom: 0;
    box-shadow: 0 30px 8px 0 black, 0 60px 20px 0 rgb(0 0 0 / 50%);
  }

  .switch .light {
    opacity: 0;
    animation: light-off 1s;
    position: absolute;
    width: 80%;
    height: 80%;
    background-image: radial-gradient(#ffc97e, transparent 40%),
      radial-gradient(circle, #ff1818 50%, transparent 80%);
  }

  .switch .dots {
    position: absolute;
    width: 100%;
    height: 100%;
    background-image: radial-gradient(transparent 30%, rgba(101, 0, 0, 0.7) 70%);
    background-size: 10px 10px;
  }

  .switch .characters {
    position: absolute;
    width: 100%;
    height: 100%;
    background: linear-gradient(white, white) 50% 20%/5% 20%,
      radial-gradient(circle, transparent 50%, white 52%, white 70%, transparent 72%) 50% 80%/33%
        25%;
    background-repeat: no-repeat;
  }

  .switch .shine {
    transition: all 0.3s cubic-bezier(1, 0, 1, 1);
    opacity: 0.3;
    position: absolute;
    width: 100%;
    height: 100%;
    background: linear-gradient(white, transparent 3%) 50% 50%/97% 97%,
      linear-gradient(
          rgba(255, 255, 255, 0.5),
          transparent 50%,
          transparent 80%,
          rgba(255, 255, 255, 0.5)
        )
        50% 50%/97% 97%;
    background-repeat: no-repeat;
  }

  .switch .shadow {
    transition: all 0.3s cubic-bezier(1, 0, 1, 1);
    opacity: 1;
    position: absolute;
    width: 100%;
    height: 100%;
    background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8));
    background-repeat: no-repeat;
  }

  @keyframes flicker {
    0% {
      opacity: 1;
    }

    80% {
      opacity: 0.8;
    }

    100% {
      opacity: 1;
    }
  }

  @keyframes light-off {
    0% {
      opacity: 1;
    }

    80% {
      opacity: 0;
    }
  }
</style>