177 lines
3.6 KiB
Vue
177 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
const settingsStore = useSettingsStore();
|
|
|
|
import gsap from "gsap";
|
|
import ShaderWorker from "~/assets/workers/ShaderWorker?worker";
|
|
|
|
const emit = defineEmits<{
|
|
ready: [];
|
|
}>();
|
|
|
|
let worker: Worker;
|
|
|
|
const canvas = ref<HTMLCanvasElement | null>(null);
|
|
const canvas_resize_temp = ref<HTMLCanvasElement | null>(null);
|
|
|
|
export interface Props {
|
|
isTurn?: boolean;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
isTurn: () => false,
|
|
});
|
|
|
|
const _colorStart = reactive([0, 0.3, 0]);
|
|
const _colorEnd = reactive([0, 1, 0]);
|
|
|
|
if (props.isTurn) {
|
|
Object.assign(_colorStart, [0, 0.3, 0]);
|
|
Object.assign(_colorEnd, [0, 1, 0]);
|
|
} else {
|
|
Object.assign(_colorStart, [0, 0.2, 0.3]);
|
|
Object.assign(_colorEnd, [0, 1, 1]);
|
|
}
|
|
|
|
function initShader() {
|
|
return new Promise<void>((resolve, reject) => {
|
|
if (!canvas.value) {
|
|
reject("canvas is null");
|
|
return;
|
|
}
|
|
const offscreen = canvas.value?.transferControlToOffscreen();
|
|
worker.postMessage(
|
|
{
|
|
command: "initShader",
|
|
canvas: offscreen,
|
|
screen: {
|
|
height: window.innerHeight * devicePixelRatio,
|
|
width: window.innerWidth * devicePixelRatio,
|
|
devicePixelRatio,
|
|
},
|
|
shaderOptions: {
|
|
quality: settingsStore.shaderQuality,
|
|
framerate: settingsStore.shaderFramerate,
|
|
},
|
|
},
|
|
[offscreen]
|
|
);
|
|
if (settingsStore.shaderFramerate == 0) {
|
|
killShader();
|
|
}
|
|
resizeShader();
|
|
worker.onmessage = (e) => {
|
|
if (e.data == "ready") {
|
|
resolve();
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
function recolorShader() {
|
|
worker.postMessage({
|
|
command: "recolorShader",
|
|
colorStart: [..._colorStart],
|
|
colorEnd: [..._colorEnd],
|
|
});
|
|
}
|
|
|
|
function resizeShader() {
|
|
worker.postMessage({
|
|
command: "resizeShader",
|
|
screen: {
|
|
height: window.innerHeight * devicePixelRatio,
|
|
width: window.innerWidth * devicePixelRatio,
|
|
devicePixelRatio,
|
|
},
|
|
shaderOptions: {
|
|
quality: settingsStore.shaderQuality,
|
|
framerate: settingsStore.shaderFramerate,
|
|
},
|
|
});
|
|
}
|
|
|
|
function killShader() {
|
|
worker.postMessage({
|
|
command: "killShader",
|
|
});
|
|
}
|
|
|
|
function resumeShader() {
|
|
worker.postMessage({
|
|
command: "resumeShader",
|
|
shaderOptions: {
|
|
quality: settingsStore.shaderQuality,
|
|
framerate: settingsStore.shaderFramerate,
|
|
},
|
|
});
|
|
}
|
|
|
|
watch(
|
|
() => [settingsStore.shaderFramerate],
|
|
() => {
|
|
if (settingsStore.shaderFramerate == 0) {
|
|
killShader();
|
|
return;
|
|
}
|
|
resumeShader();
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => [settingsStore.shaderQuality],
|
|
() => {
|
|
resizeShader();
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => [props.isTurn],
|
|
() => {
|
|
if (props.isTurn) {
|
|
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: 0.3, 2: 0 });
|
|
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 0 });
|
|
} else {
|
|
gsap.to(_colorStart, { duration: 0.5, 0: 0, 1: 0.2, 2: 0.3 });
|
|
gsap.to(_colorEnd, { duration: 0.5, 0: 0, 1: 1, 2: 1 });
|
|
}
|
|
}
|
|
);
|
|
|
|
watch([_colorEnd, _colorStart], () => {
|
|
recolorShader();
|
|
});
|
|
|
|
onMounted(() => {
|
|
worker = new ShaderWorker();
|
|
recolorShader();
|
|
initShader().then(() => {
|
|
emit("ready");
|
|
});
|
|
window.addEventListener("resize", resizeShader, true);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener("resize", resizeShader);
|
|
killShader();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<canvas ref="canvas" id="background" :class="{ pixelated: settingsStore.shaderPixelated }" width="480"
|
|
height="270"></canvas>
|
|
</template>
|
|
|
|
<style scoped>
|
|
#background {
|
|
left: 0;
|
|
position: fixed;
|
|
height: 100%;
|
|
width: 100%;
|
|
z-index: -999999;
|
|
}
|
|
|
|
#background.pixelated {
|
|
image-rendering: pixelated;
|
|
}
|
|
</style>
|