mirror of
https://github.com/vrtmrz/ninja-cursor.git
synced 2026-07-22 06:40:31 +00:00
Fixed:
- Now cursor is right position while scrolling. - Hide if non-editable element.
This commit is contained in:
parent
37a17b1c29
commit
f1f018e983
2 changed files with 153 additions and 111 deletions
90
main.ts
90
main.ts
|
|
@ -23,8 +23,30 @@ class NinjaCursorForWindow {
|
|||
ad.body.appendChild(this.wrapperElement);
|
||||
this.cursorElement.addClass("x-cursor");
|
||||
const root = ad.documentElement;
|
||||
|
||||
const moveCursor = () => {
|
||||
let datumTop = 0;
|
||||
let datumElement: HTMLElement;
|
||||
let cursorVisibility = false;
|
||||
const moveCursor = (e?: Event, noAnimate?: boolean) => {
|
||||
if (e && e.target instanceof HTMLElement && (e.target.isContentEditable || e.target.tagName == "INPUT")) {
|
||||
// If it caused by clicking an element and it is editable.
|
||||
datumElement = e.target;
|
||||
if (!cursorVisibility) {
|
||||
root.style.setProperty("--cursor-visibility", `visible`);
|
||||
cursorVisibility = true;
|
||||
}
|
||||
} else if (e != null) {
|
||||
// If it caused by clicking an element but it is not editable.
|
||||
if (cursorVisibility) {
|
||||
root.style.setProperty("--cursor-visibility", `hidden`);
|
||||
cursorVisibility = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (e && e.target instanceof HTMLElement) {
|
||||
// Memo datum element for scroll.
|
||||
datumElement = e.target;
|
||||
}
|
||||
datumTop = datumElement.getBoundingClientRect().top;
|
||||
const selection = aw.getSelection();
|
||||
if (!selection) {
|
||||
console.log("Could not find selection");
|
||||
|
|
@ -45,15 +67,15 @@ class NinjaCursorForWindow {
|
|||
if (textRect.x == 0 && textRect.y == 0) {
|
||||
textRange.setStart(range.endContainer, range.endOffset - 1);
|
||||
textRange.setEnd(range.endContainer, range.endOffset);
|
||||
const textRectx = textRange.getClientRects();
|
||||
const txx = textRectx.item(textRectx.length - 1);
|
||||
if (!txx) {
|
||||
const textRects = textRange.getClientRects();
|
||||
const tempRect = textRects.item(textRects.length - 1);
|
||||
if (!tempRect) {
|
||||
console.log("Could not found");
|
||||
return;
|
||||
}
|
||||
textRect = txx;
|
||||
textRect.x = txx.right;
|
||||
textRect.y = txx.bottom - txx.height;
|
||||
textRect = tempRect;
|
||||
textRect.x = tempRect.right;
|
||||
textRect.y = tempRect.bottom - tempRect.height;
|
||||
}
|
||||
|
||||
if (textRect.x == 0 && textRect.y == 0) {
|
||||
|
|
@ -78,6 +100,7 @@ class NinjaCursorForWindow {
|
|||
Math.abs(Math.sin(cursorDragAngle)) * 8 +
|
||||
Math.abs(Math.cos(cursorDragAngle)) * rect.height;
|
||||
const cursorDragWidth = cursorDragDistance;
|
||||
|
||||
root.style.setProperty(
|
||||
"--cursor-drag-height",
|
||||
`${cursorDragHeight}px`
|
||||
|
|
@ -92,13 +115,19 @@ class NinjaCursorForWindow {
|
|||
);
|
||||
root.style.setProperty("--cursor-height", `${rect.height}px`);
|
||||
root.style.setProperty("--cursor-x1", `${this.lastPos.x}px`);
|
||||
root.style.setProperty("--cursor-y1", `${this.lastPos.y}px`);
|
||||
root.style.setProperty("--cursor-y1src", `${this.lastPos.y}px`);
|
||||
root.style.setProperty("--cursor-x2", `${rect.x}px`);
|
||||
root.style.setProperty("--cursor-y2", `${rect.y}px`);
|
||||
root.style.setProperty("--cursor-y2src", `${rect.y}px`);
|
||||
root.style.setProperty("--cursor-offset-y", `${0}px`);
|
||||
if (noAnimate) {
|
||||
this.lastPos = rect;
|
||||
return;
|
||||
}
|
||||
this.cursorElement.removeClass("x-cursor0");
|
||||
this.cursorElement.removeClass("x-cursor1");
|
||||
this.cursorElement.getAnimations().forEach((anim) => anim.cancel());
|
||||
|
||||
datumTop = datumElement.getBoundingClientRect().top;
|
||||
aw.requestAnimationFrame((time) => {
|
||||
this.cursorElement.addClass(`x-cursor${this.styleCount}`);
|
||||
this.lastPos = rect;
|
||||
|
|
@ -106,23 +135,30 @@ class NinjaCursorForWindow {
|
|||
};
|
||||
|
||||
|
||||
registerDomEvent(aw, "keydown", () => {
|
||||
moveCursor();
|
||||
});
|
||||
registerDomEvent(aw, "keyup", () => {
|
||||
moveCursor();
|
||||
});
|
||||
registerDomEvent(aw, "mousedown", () => {
|
||||
moveCursor();
|
||||
});
|
||||
registerDomEvent(aw, "mouseup", () => {
|
||||
moveCursor();
|
||||
});
|
||||
registerDomEvent(aw, "touchend", () => {
|
||||
moveCursor();
|
||||
});
|
||||
registerDomEvent(aw, "touchstart", () => {
|
||||
moveCursor();
|
||||
const supportVIMMode = true;
|
||||
const eventNames = ["keydown", "mousedown", "touchend", ...(supportVIMMode ? ["keyup", "mouseup", "touchstart"] : [])];
|
||||
for (const event of eventNames) {
|
||||
registerDomEvent(aw, event, (ev: Event) => {
|
||||
moveCursor(ev);
|
||||
});
|
||||
}
|
||||
// Handles scroll till scroll is finish.
|
||||
const applyWheelScroll = (last?: number | boolean) => {
|
||||
requestAnimationFrame(() => {
|
||||
if (datumElement) {
|
||||
const curTop = datumElement.getBoundingClientRect().top;
|
||||
const diff = curTop - datumTop;
|
||||
root.style.setProperty("--cursor-offset-y", `${diff}px`);
|
||||
if (last === false || last != diff) {
|
||||
requestAnimationFrame(() => applyWheelScroll(diff));
|
||||
} else if (last == diff) {
|
||||
moveCursor(undefined, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
registerDomEvent(aw, "wheel", (e: WheelEvent) => {
|
||||
applyWheelScroll(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
174
styles.css
174
styles.css
|
|
@ -1,114 +1,120 @@
|
|||
.x-cursor {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: var(--cursor-height);
|
||||
width: 8px;
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
background: var(--text-normal);
|
||||
transform: translate3d(var(--cursor-x2), var(--cursor-y2), 0);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: var(--cursor-height);
|
||||
width: 8px;
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
background: var(--text-normal);
|
||||
transform: translate3d(var(--cursor-x2), var(--cursor-y2), 0);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
visibility: var(--cursor-visibility);
|
||||
}
|
||||
|
||||
.x-cursor:after {
|
||||
content: "";
|
||||
height: var(--cursor-drag-height);
|
||||
width: var(--cursor-drag-width);
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
|
||||
background: linear-gradient(to right, var(--text-normal), transparent);
|
||||
transform: rotate(var(--cursor-drag-angle));
|
||||
transform-origin: 4px center;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
height: var(--cursor-drag-height);
|
||||
width: var(--cursor-drag-width);
|
||||
display: inline-block;
|
||||
user-select: none;
|
||||
background: linear-gradient(to right, var(--text-normal), transparent);
|
||||
transform: rotate(var(--cursor-drag-angle));
|
||||
transform-origin: 4px center;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.x-cursor0 {
|
||||
animation: a1 75ms cubic-bezier(0.34, 1.26, 0.84, 1) 0s both;
|
||||
animation: a1 75ms cubic-bezier(0.34, 1.26, 0.84, 1) 0s both;
|
||||
}
|
||||
|
||||
.x-cursor1 {
|
||||
animation: a2 75ms cubic-bezier(0.34, 1.26, 0.84, 1) 0s both;
|
||||
animation: a2 75ms cubic-bezier(0.34, 1.26, 0.84, 1) 0s both;
|
||||
}
|
||||
|
||||
:root {
|
||||
--cursor-x1: "0px";
|
||||
--cursor-x2: "0px";
|
||||
--cursor-y1: "0px";
|
||||
--cursor-y2: "0px";
|
||||
--cursor-drag-distance: "0px";
|
||||
--cursor-drag-angle: "0deg";
|
||||
--cursor-drag-width: "0px";
|
||||
--cursor-drag-height: "0px";
|
||||
--cursor-x1: "0px";
|
||||
--cursor-x2: "0px";
|
||||
--cursor-y1src: "0px";
|
||||
--cursor-y2src: "0px";
|
||||
--cursor-offset-y: "0px";
|
||||
--cursor-drag-distance: "0px";
|
||||
--cursor-drag-angle: "0deg";
|
||||
--cursor-drag-width: "0px";
|
||||
--cursor-drag-height: "0px";
|
||||
|
||||
--cursor-height: 18px;
|
||||
--cursor-height: 18px;
|
||||
--cursor-y1: calc(var(--cursor-y1src) + var(--cursor-offset-y));
|
||||
--cursor-y2: calc(var(--cursor-y2src) + var(--cursor-offset-y));
|
||||
--cursor-visibility: "hidden";
|
||||
/* --cursor-color: --text-normal; */
|
||||
}
|
||||
|
||||
.cursorWrapper {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
background-color: transparent;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
background-color: transparent;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@keyframes a1 {
|
||||
0% {
|
||||
transform: translate3d(var(--cursor-x1), var(--cursor-y1), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
transform: translate3d(var(--cursor-x1), var(--cursor-y1), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
4% {
|
||||
opacity: 1;
|
||||
}
|
||||
4% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
opacity: 0.95;
|
||||
}
|
||||
80% {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translate3d(var(--cursor-x2), var(--cursor-y2), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0.9;
|
||||
}
|
||||
90% {
|
||||
transform: translate3d(var(--cursor-x2), var(--cursor-y2), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes a2 {
|
||||
0% {
|
||||
transform: translate3d(var(--cursor-x1), var(--cursor-y1), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
transform: translate3d(var(--cursor-x1), var(--cursor-y1), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
4% {
|
||||
opacity: 1;
|
||||
}
|
||||
4% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
opacity: 0.95;
|
||||
}
|
||||
80% {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translate3d(var(--cursor-x2), var(--cursor-y2), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0.9;
|
||||
}
|
||||
90% {
|
||||
transform: translate3d(var(--cursor-x2), var(--cursor-y2), 0);
|
||||
background-color: var(--text-normal);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue