187 lines
No EOL
3.7 KiB
Vue
187 lines
No EOL
3.7 KiB
Vue
<template>
|
|
<label class="checkbox">
|
|
<input
|
|
:id="id"
|
|
type="checkbox"
|
|
:checked="modelValue"
|
|
@change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
|
|
/>
|
|
<span class="checkbox__box">
|
|
<span class="checkbox__mark"></span>
|
|
</span>
|
|
<span
|
|
class="checkbox__label"
|
|
:data-text="label"
|
|
>
|
|
{{ label }}
|
|
</span>
|
|
</label>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const props = defineProps<{
|
|
label: string;
|
|
modelValue: boolean;
|
|
id: number;
|
|
}>();
|
|
|
|
defineEmits<{
|
|
'update:modelValue': [value: boolean]
|
|
}>();
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.checkbox {
|
|
width: fit-content;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
position: relative;
|
|
|
|
--glitch-anim-duration: 0.3s;
|
|
|
|
& input {
|
|
position: absolute;
|
|
opacity: 0;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
|
|
&__box {
|
|
width: 1.5em;
|
|
height: 1.5em;
|
|
border: 2px solid $accentNeon;
|
|
position: relative;
|
|
transition: all 0.3s ease;
|
|
|
|
clip-path: polygon(
|
|
15% 0,
|
|
85% 0,
|
|
100% 15%,
|
|
100% 85%,
|
|
85% 100%,
|
|
15% 100%,
|
|
0 85%,
|
|
0 15%
|
|
);
|
|
}
|
|
|
|
&__mark {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 60%;
|
|
height: 60%;
|
|
background-color: $accentNeon;
|
|
transform: translate(-50%, -50%) scale(0);
|
|
opacity: 0;
|
|
transition: all 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
|
clip-path: inherit;
|
|
}
|
|
|
|
&__label {
|
|
color: $white;
|
|
font-weight: 500;
|
|
font-size: 18px;
|
|
text-transform: uppercase;
|
|
position: relative;
|
|
transition:
|
|
color 0.3s ease,
|
|
text-shadow 0.3s ease;
|
|
}
|
|
}
|
|
|
|
.checkbox input:checked + .checkbox__box .checkbox__mark {
|
|
transform: translate(-50%, -50%) scale(1);
|
|
opacity: 1;
|
|
animation: glitch-anim-checkbox var(--glitch-anim-duration) both;
|
|
}
|
|
|
|
.checkbox input:checked ~ .checkbox__label {
|
|
color: $accentNeon;
|
|
text-shadow: 0 0 8px $accentNeon;
|
|
}
|
|
|
|
.checkbox:hover .checkbox__box {
|
|
box-shadow: 0 0 10px $accentNeon;
|
|
}
|
|
|
|
.checkbox:hover .checkbox__label::before,
|
|
.checkbox:hover .checkbox__label::after {
|
|
content: attr(data-text);
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: #050505;
|
|
}
|
|
|
|
.checkbox:hover .checkbox__label::before {
|
|
color: #a855f7;
|
|
animation: glitch-anim-text var(--glitch-anim-duration)
|
|
cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
|
}
|
|
|
|
.checkbox:hover .checkbox__label::after {
|
|
color: $accentNeon;
|
|
animation: glitch-anim-text var(--glitch-anim-duration)
|
|
cubic-bezier(0.25, 0.46, 0.45, 0.94) reverse both;
|
|
}
|
|
|
|
@keyframes glitch-anim-checkbox {
|
|
0% {
|
|
transform: translate(-50%, -50%);
|
|
clip-path: inset(0 0 0 0);
|
|
}
|
|
20% {
|
|
transform: translate(calc(-50% - 3px), calc(-50% + 2px));
|
|
clip-path: inset(50% 0 20% 0);
|
|
}
|
|
40% {
|
|
transform: translate(calc(-50% + 2px), calc(-50% - 1px));
|
|
clip-path: inset(20% 0 60% 0);
|
|
}
|
|
60% {
|
|
transform: translate(calc(-50% - 2px), calc(-50% + 1px));
|
|
clip-path: inset(80% 0 5% 0);
|
|
}
|
|
80% {
|
|
transform: translate(calc(-50% + 2px), calc(-50% - 2px));
|
|
clip-path: inset(30% 0 45% 0);
|
|
}
|
|
100% {
|
|
transform: translate(-50%, -50%);
|
|
clip-path: inset(0 0 0 0);
|
|
}
|
|
}
|
|
|
|
@keyframes glitch-anim-text {
|
|
0% {
|
|
transform: translate(0);
|
|
clip-path: inset(0 0 0 0);
|
|
}
|
|
20% {
|
|
transform: translate(-3px, 2px);
|
|
clip-path: inset(50% 0 20% 0);
|
|
}
|
|
40% {
|
|
transform: translate(2px, -1px);
|
|
clip-path: inset(20% 0 60% 0);
|
|
}
|
|
60% {
|
|
transform: translate(-2px, 1px);
|
|
clip-path: inset(80% 0 5% 0);
|
|
}
|
|
80% {
|
|
transform: translate(2px, -2px);
|
|
clip-path: inset(30% 0 45% 0);
|
|
}
|
|
100% {
|
|
transform: translate(0);
|
|
clip-path: inset(0 0 0 0);
|
|
}
|
|
}
|
|
</style> |