Follow Mouse MFM

This commit is contained in:
nelle 2024-07-14 06:11:58 -06:00
parent 846baf305b
commit d41a81f600
2 changed files with 105 additions and 0 deletions

View file

@ -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>

View file

@ -1,6 +1,7 @@
import { defineComponent, h } from "vue"; import { defineComponent, h } from "vue";
import * as mfm from "@transfem-org/sfm-js"; import * as mfm from "@transfem-org/sfm-js";
import type { VNode } from "vue"; import type { VNode } from "vue";
import CkFollowMouse from "./CkFollowMouse.vue";
import MkUrl from "@/components/global/MkUrl.vue"; import MkUrl from "@/components/global/MkUrl.vue";
import MkTime from '@/components/global/MkTime.vue'; import MkTime from '@/components/global/MkTime.vue';
import MkLink from "@/components/MkLink.vue"; import MkLink from "@/components/MkLink.vue";
@ -275,6 +276,24 @@ export default defineComponent({
style = `transform: ${rotate}(${degrees}deg); transform-origin: center center;`; style = `transform: ${rotate}(${degrees}deg); transform-origin: center center;`;
break; 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": { case "position": {
const x = parseFloat(token.props.args.x ?? "0"); const x = parseFloat(token.props.args.x ?? "0");
const y = parseFloat(token.props.args.y ?? "0"); const y = parseFloat(token.props.args.y ?? "0");