jormungandr-patches/patches/newmfm/0004-Follow-Mouse-MFM.patch
2024-07-14 20:32:39 -06:00

143 lines
3.9 KiB
Diff

From d41a81f60051c197ca98db912d706745edd002d7 Mon Sep 17 00:00:00 2001
From: limepotato <limepot@protonmail.ch>
Date: Sun, 14 Jul 2024 06:11:58 -0600
Subject: [PATCH 4/5] Follow Mouse MFM
---
.../client/src/components/CkFollowMouse.vue | 86 +++++++++++++++++++
packages/client/src/components/mfm.ts | 19 ++++
2 files changed, 105 insertions(+)
create mode 100644 packages/client/src/components/CkFollowMouse.vue
diff --git a/packages/client/src/components/CkFollowMouse.vue b/packages/client/src/components/CkFollowMouse.vue
new file mode 100644
index 000000000..ce7e3c79a
--- /dev/null
+++ b/packages/client/src/components/CkFollowMouse.vue
@@ -0,0 +1,86 @@
+<!--
+SPDX-FileCopyrightText: leah and other Cutiekey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<span ref="container" :class="$style.root">
+ <span ref="el" :class="$style.inner" style="position: absolute">
+ <slot></slot>
+ </span>
+</span>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, onUnmounted, shallowRef } from 'vue';
+const el = shallowRef<HTMLElement>();
+const container = shallowRef<HTMLElement>();
+const props = defineProps({
+ x: {
+ type: Boolean,
+ default: true,
+ },
+ y: {
+ type: Boolean,
+ default: true,
+ },
+ speed: {
+ type: String,
+ default: '0.1s',
+ },
+ rotateByVelocity: {
+ type: Boolean,
+ default: true,
+ },
+});
+
+let lastX = 0;
+let lastY = 0;
+let oldAngle = 0;
+
+function lerp(a, b, alpha) {
+ return a + alpha * (b - a);
+}
+
+const updatePosition = (mouseEvent: MouseEvent) => {
+ if (el.value && container.value) {
+ const containerRect = container.value.getBoundingClientRect();
+ const newX = mouseEvent.clientX - containerRect.left;
+ const newY = mouseEvent.clientY - containerRect.top;
+ let transform = `translate(calc(${props.x ? newX : 0}px - 50%), calc(${props.y ? newY : 0}px - 50%))`;
+ if (props.rotateByVelocity) {
+ const deltaX = newX - lastX;
+ const deltaY = newY - lastY;
+ const angle = lerp(
+ oldAngle,
+ Math.atan2(deltaY, deltaX) * (180 / Math.PI),
+ 0.1,
+ );
+ transform += ` rotate(${angle}deg)`;
+ oldAngle = angle;
+ }
+ el.value.style.transform = transform;
+ el.value.style.transition = `transform ${props.speed}`;
+ lastX = newX;
+ lastY = newY;
+ }
+};
+
+onMounted(() => {
+ window.addEventListener('mousemove', updatePosition);
+});
+
+onUnmounted(() => {
+ window.removeEventListener('mousemove', updatePosition);
+});
+</script>
+
+<style lang="scss" module>
+.root {
+ position: relative;
+ display: inline-block;
+}
+.inner {
+ transform-origin: center center;
+}
+</style>
diff --git a/packages/client/src/components/mfm.ts b/packages/client/src/components/mfm.ts
index 382b1b9e7..25faf680b 100644
--- a/packages/client/src/components/mfm.ts
+++ b/packages/client/src/components/mfm.ts
@@ -1,6 +1,7 @@
import { defineComponent, h } from "vue";
import * as mfm from "@transfem-org/sfm-js";
import type { VNode } from "vue";
+import CkFollowMouse from "./CkFollowMouse.vue";
import MkUrl from "@/components/global/MkUrl.vue";
import MkTime from '@/components/global/MkTime.vue';
import MkLink from "@/components/MkLink.vue";
@@ -275,6 +276,24 @@ export default defineComponent({
style = `transform: ${rotate}(${degrees}deg); transform-origin: center center;`;
break;
}
+ case 'followmouse': {
+ // Make sure advanced MFM is on and that reduced motion is off
+
+ let x = (!!token.props.args.x);
+ let y = (!!token.props.args.y);
+
+ if (!x && !y) {
+ x = true;
+ y = true;
+ }
+
+ return h(CkFollowMouse, {
+ x: x,
+ y: y,
+ speed: validTime(token.props.args.speed) ?? '0.1s',
+ rotateByVelocity: !!token.props.args.rotateByVelocity,
+ }, genEl(token.children));
+ }
case "position": {
const x = parseFloat(token.props.args.x ?? "0");
const y = parseFloat(token.props.args.y ?? "0");
--
2.45.2