ADD: LED Ring & LED Keys in preview
This commit is contained in:
11
package-lock.json
generated
11
package-lock.json
generated
@@ -20,6 +20,7 @@
|
|||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
|
"gsap": "^3.12.5",
|
||||||
"lucide-vue-next": "^0.309.0",
|
"lucide-vue-next": "^0.309.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"radix-vue": "^1.3.0",
|
"radix-vue": "^1.3.0",
|
||||||
@@ -6818,6 +6819,11 @@
|
|||||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/gsap": {
|
||||||
|
"version": "3.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
|
||||||
|
"integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ=="
|
||||||
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
@@ -16130,6 +16136,11 @@
|
|||||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"gsap": {
|
||||||
|
"version": "3.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz",
|
||||||
|
"integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ=="
|
||||||
|
},
|
||||||
"has-flag": {
|
"has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"electron-squirrel-startup": "^1.0.0",
|
"electron-squirrel-startup": "^1.0.0",
|
||||||
|
"gsap": "^3.12.5",
|
||||||
"lucide-vue-next": "^0.309.0",
|
"lucide-vue-next": "^0.309.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"radix-vue": "^1.3.0",
|
"radix-vue": "^1.3.0",
|
||||||
|
|||||||
23
src/components/device/DeviceKeys.vue
Normal file
23
src/components/device/DeviceKeys.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex">
|
||||||
|
<div
|
||||||
|
v-for="(key, index) in keys" :key="key" class="aspect-square flex-1 rounded-[2px] flex items-center justify-center"
|
||||||
|
:style="`box-shadow: 0 0 20px 0 hsl(${-hue + index * 20},100%,50%)`">
|
||||||
|
<div class="font-heading text-2xl text-black opacity-30">{{ key}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import gsap from 'gsap'
|
||||||
|
|
||||||
|
const keys = ref(['a', 'b', 'c', 'd'])
|
||||||
|
|
||||||
|
const hue = ref(0)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
gsap.to(hue, { duration: 10, value: 360, repeat: -1, ease: 'none' })
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -1,37 +1,62 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg :width="size" :height="size" filter="url(#blur)" class="mix-blend-screen">
|
<svg :viewBox="`0 0 ${size} ${size}`" filter="url(#blur)">
|
||||||
<filter id="blur" color-interpolation-filters="sRGB">
|
<filter id="blur" color-interpolation-filters="sRGB">
|
||||||
<feGaussianBlur
|
<feGaussianBlur
|
||||||
v-for="index in blurSteps" :key="index" in="SourceGraphic" :stdDeviation="blur*index"
|
v-for="index in blurSteps" :key="index" in="SourceGraphic" :stdDeviation="blur*index"
|
||||||
:result="index" />
|
:result="index" />
|
||||||
<feMerge result="blurMerge">
|
<feMerge result="blurMerge">
|
||||||
<feMergeNode v-for="index in blurSteps" :key="index" :in="index" />
|
<feMergeNode v-for="index in blurSteps" :key="index" :in="index" />
|
||||||
<feMergeNode in="SourceGraphic" />
|
|
||||||
</feMerge>
|
</feMerge>
|
||||||
</filter>
|
</filter>
|
||||||
<circle
|
<circle
|
||||||
v-for="index in ledCount" :key="index"
|
v-for="index in ledCount" :key="index"
|
||||||
:r="radius"
|
:transform="`rotate(${index/ledCount*360} ${size/2} ${size/2})`"
|
||||||
|
:r="ledRadius"
|
||||||
:cx="size/2"
|
:cx="size/2"
|
||||||
:cy="size/2"
|
:cy="padding + ledRadius"
|
||||||
fill="none"
|
:fill="leds[index-1]?.hex()" />
|
||||||
:stroke-width="ledWidth"
|
|
||||||
:stroke-dashoffset="index/ledCount*ledArcSize"
|
|
||||||
:stroke-dasharray="`${ledArcSize/ledCount} ${ledArcSize}`"
|
|
||||||
:stroke="`rgb(${index/ledCount*255} ${index/ledCount*255-128} ${0})`" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import Color from 'color'
|
||||||
|
|
||||||
const radius = ref(120)
|
const props = defineProps({
|
||||||
const ledWidth = ref(5)
|
value: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const leds = ref(new Array(60).fill(Color()))
|
||||||
|
|
||||||
|
const radius = ref(100)
|
||||||
|
const ledRadius = ref(3)
|
||||||
const ledCount = ref(60)
|
const ledCount = ref(60)
|
||||||
const blur = ref(10)
|
const blur = ref(2)
|
||||||
const blurSteps = ref(3)
|
const blurSteps = ref(5)
|
||||||
const padding = ref(20)
|
const padding = ref(40)
|
||||||
|
|
||||||
const size = computed(() => (radius.value + padding.value) * 2 + ledWidth.value)
|
const size = computed(() => (radius.value + ledRadius.value + padding.value) * 2)
|
||||||
const ledArcSize = computed(() => 2 * Math.PI * radius.value)
|
|
||||||
|
|
||||||
|
let interval = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
interval = setInterval(() => {
|
||||||
|
const valueIndex = Math.floor(props.value / 100 * ledCount.value)
|
||||||
|
leds.value.forEach((color, index) => {
|
||||||
|
const distance = Math.abs(index - valueIndex) % ledCount.value
|
||||||
|
if (distance < 1) {
|
||||||
|
leds.value[index] = Color.hsl(40, 100, 100)
|
||||||
|
} else if (distance < 2) {
|
||||||
|
leds.value[index] = Color.hsl(40, 100, 60)
|
||||||
|
} else {
|
||||||
|
leds.value[index] = color.mix(Color.hsl(310, 100, 20), 0.03)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(interval)
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -9,12 +9,13 @@
|
|||||||
<ScrambleText :text="store.selectedProfile.name" />
|
<ScrambleText :text="store.selectedProfile.name" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<DeviceLEDRing :value="barValue" class="absolute h-[66%] top-[12.5%] left-0 right-0 mx-auto" />
|
||||||
<div
|
<div
|
||||||
class="rounded-full aspect-square absolute h-[30%] top-[30.5%] left-0 right-0 mx-auto flex flex-col justify-center items-center overflow-hidden"
|
class="rounded-full aspect-square absolute h-[30%] top-[30.5%] left-0 right-0 mx-auto flex flex-col justify-center items-center overflow-hidden"
|
||||||
style="background: linear-gradient(45deg, black 30%, #252525 50%, #232323 60%, black)">
|
style="background: linear-gradient(45deg, black 30%, #252525 50%, #232323 60%, black)">
|
||||||
<div class="flex flex-col items-center text-center pb-2 mix-blend-screen">
|
<div class="flex flex-col items-center text-center pb-2 mix-blend-screen">
|
||||||
<img :src="LogoMidi" alt="midi-logo" class="opacity-50 h-4">
|
<img :src="LogoMidi" alt="midi-logo" class="opacity-50 h-4">
|
||||||
<h2 class="font-pixellg text-5xl">{{ value }}</h2>
|
<h2 class="font-pixellg text-5xl">{{ parseInt(value) }}</h2>
|
||||||
<div class="font-pixelsm text-md">HIGH PASS</div>
|
<div class="font-pixelsm text-md">HIGH PASS</div>
|
||||||
<DeviceBar v-model="barValue" :count="30" :width="120" />
|
<DeviceBar v-model="barValue" :count="30" :width="120" />
|
||||||
<span
|
<span
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<DeviceKeys class="absolute w-[72.7%] top-[77.2%] gap-[2.8%] left-0 right-0 mx-auto" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -33,6 +35,9 @@ import DeviceBar from '@/components/device/DeviceBar.vue'
|
|||||||
import { useStore } from '@/store'
|
import { useStore } from '@/store'
|
||||||
import ScrambleText from '@/components/effects/ScrambleText.vue'
|
import ScrambleText from '@/components/effects/ScrambleText.vue'
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import DeviceLEDRing from '@/components/device/DeviceLEDRing.vue'
|
||||||
|
import gsap from 'gsap'
|
||||||
|
import DeviceKeys from '@/components/device/DeviceKeys.vue'
|
||||||
|
|
||||||
const value = ref(69)
|
const value = ref(69)
|
||||||
|
|
||||||
@@ -40,28 +45,14 @@ const barValue = computed(() => value.value / 127 * 100)
|
|||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|
||||||
let anim = null
|
const targetValue = ref(69)
|
||||||
let step = null
|
const animateValue = () => {
|
||||||
|
targetValue.value = Math.floor(Math.random() * 127)
|
||||||
|
gsap.to(value, { duration: 1, value: targetValue.value, ease: 'power2.inOut' })
|
||||||
|
setTimeout(animateValue, 1500 + Math.random() * 2000)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
anim = setInterval(() => {
|
animateValue()
|
||||||
clearInterval(step)
|
|
||||||
const target = Math.floor(Math.random() * 127)
|
|
||||||
step = setInterval(() => {
|
|
||||||
const intVal = Math.floor(value.value)
|
|
||||||
if (intVal < target) {
|
|
||||||
value.value = intVal + 1
|
|
||||||
} else if (intVal > target) {
|
|
||||||
value.value = intVal - 1
|
|
||||||
} else {
|
|
||||||
clearInterval(step)
|
|
||||||
}
|
|
||||||
}, 20)
|
|
||||||
}, 2000)
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(anim)
|
|
||||||
clearInterval(step)
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
75
src/data/mapping.json
Normal file
75
src/data/mapping.json
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"actions": {
|
||||||
|
"unique-id-12313": {
|
||||||
|
"trigger": "rotation",
|
||||||
|
// "rotation", "keyDown", "keyUp", "keyHold"
|
||||||
|
"key": "a",
|
||||||
|
"duration": 250,
|
||||||
|
// ms (only for keyHold)
|
||||||
|
"direction": "clockwise",
|
||||||
|
// "counterClockwise" (only for rotation),
|
||||||
|
"condition": {
|
||||||
|
"type": "key",
|
||||||
|
"key": "a"
|
||||||
|
},
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"type": "keyDown",
|
||||||
|
// "keyUp"
|
||||||
|
"key": "ctrl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "serialWrite",
|
||||||
|
"data": "Hello, World!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "keyCombination",
|
||||||
|
"keys": [
|
||||||
|
"ctrl",
|
||||||
|
"a"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "delay",
|
||||||
|
"time": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "mouseMove",
|
||||||
|
"x": 100,
|
||||||
|
"y": 20,
|
||||||
|
"smooth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "midiCC",
|
||||||
|
"cc": 64,
|
||||||
|
"value": 127
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "midiNote",
|
||||||
|
"note": 60,
|
||||||
|
"velocity": 127
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "midiProgramChange",
|
||||||
|
"program": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "action",
|
||||||
|
// watch out for infinite loops
|
||||||
|
"action": "unique-id-42069"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "profileChange",
|
||||||
|
"profile": "+1",
|
||||||
|
// "profile-name"
|
||||||
|
"tag": null
|
||||||
|
// "tag-name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "repeat",
|
||||||
|
"times": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user