I wanted to try to do the angular version of a cursor follow movement, but the mouse movement is not smooth, how do I fix this and what's the reason for it?:
https://stackoverflow.com/a/48756322/5152892
My angular attempt:
https://stackblitz.com/edit/angular-vnx9yd?file=src/app/app.component.ts
export class AppComponent {
name = 'Angular';
constructor(
private el: ElementRef
) {}
get tooltip() {
return this.el.nativeElement.querySelector('.sites-circle');
}
enter(source: string) {
this.tooltip.classList.add('show');
}
move(e: { pageX: number; pageY: number }) {
const tooltipStyle = this.tooltip.style;
tooltipStyle.left = e.pageX + 'px';
tooltipStyle.top = e.pageY + 'px';
}
leave() {
this.tooltip.classList.remove('show');
}
}
CSS:
p {
font-family: Lato;
}
.mouse-circle {
position: absolute;
border: solid 1px #ccc;
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
}
.show {
opacity: 1 !important;
}
.sites-circle {
z-index: 500;
position: absolute;
border: solid 1px #ccc;
width: 20px;
height: 20px;
border-radius: 50%;
background: blue;
pointer-events: none;
transition-duration: 200ms;
transition-timing-function: ease-out;
transform: translate(-50%, -50%);
opacity: 0;
}
.wrapper {
height: 100vh;
width: 100%;
}
HTML:
<div
class="wrapper"
(mouseenter)="enter()"
(mousemove)="move($event)"
(mouseleave)="leave()"
>
<p>Start editing to see some magic happen :)</p>
<div class="sites-circle">hello</div>
</div>
sometimes is better use fromEvent rxjs operator to mannage mousemove. This allow use different operators to not emit constantly the event
Use Viewchild to get the elements
<div #mydiv class="show"
class="wrapper"
(mouseenter)="enter()"
(mouseleave)="leave()"
>
<div #tooltip class="sites-circle">hello</div>
</div>
#ViewChild('mydiv',{static:true}) div:ElementRef
#ViewChild('tooltip',{static:true}) tooltipEl:ElementRef
Then
ngOnInit()
{
this.subscription=fromEvent(this.div.nativeElement,'mousemove').pipe(
throttleTime(200,asyncScheduler,{ trailing: true }))
.subscribe((e:any)=>{
const tooltipStyle = this.tooltip.style;
tooltipStyle.left = e.pageX + 'px';
tooltipStyle.top = e.pageY + 'px';
this.cont++;
})
}
ngOnDestroy()
{
this.subscription.unsubscribe()
}
In the e.g. (see stackblitz) I use throttleTime to not emit a new event until 200 miliseconds (you can change to 100 or 150)
Related
I am resizing and positioning a box using the mousemove event. Those. i change transform translate and width (height) with pageX (pageY). But due to the fact that the mouse event mousemove does not always have time to be processed (for example, if you move the mouse quickly) or does not have time to read conditions, the block goes out of bounds.
Question: what do I need to do in this case so that the block does not go beyond the boundaries?
This is how it looks roughly. Those. in this example, the second_block is outside the first_block (500px), i.e. it does not have time to read the condition. How should this issue be resolved? Also for convenience https://jsfiddle.net/ManuOP/t1r4szdx/3/
<div id="first_block" class="first_block">
<div id="auxiliary_block">
<div id="second_block" class="second_block"></div>
<input id="point" class="point" name="name_point" type="button">
</div>
</div>
<script src="1.block_in_center_question.js"></script>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
display: flex;
flex-direction: column;
}
div.first_block {
height: 300px;
width: 500px;
background: green;
}
div#auxiliary_block {
position: absolute;
}
div.second_block {
height: 200px;
width: 300px;
background: orange;
}
input.point {
position: absolute;
cursor: pointer;
height: 14px;
width: 14px;
border: none;
background: black;
right: -7px;
top: 50%;
}
"use strict";
let second_block = document.getElementById('second_block');
let point = document.getElementById('point');
function change_second_block() {
if(second_block.clientWidth < 500) {
second_block.style.width = `${start_x + event.pageX}px`;
}
}
point.addEventListener('mousedown', (event) => {
window.start_x = second_block.clientWidth - event.pageX;
document.addEventListener('mousemove', change_second_block);
});
You could just test the new width and if it's too large then constrain it to be no more than the maximum.
This snippet does this for the x direction and forces it to remain at or below 500px.
"use strict";
let second_block = document.getElementById('second_block');
let point = document.getElementById('point');
function change_second_block() {
if (second_block.clientWidth < 500) {
second_block.style.width = (start_x + event.pageX) < 500 ? `${start_x + event.pageX}px` : '500px';
}
}
point.addEventListener('mousedown', (event) => {
window.start_x = second_block.clientWidth - event.pageX;
document.addEventListener('mousemove', change_second_block);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div {
display: flex;
flex-direction: column;
}
div.first_block {
height: 300px;
width: 500px;
background: green;
}
div#auxiliary_block {
position: absolute;
}
div.second_block {
height: 200px;
width: 300px;
background: orange;
}
input.point {
position: absolute;
cursor: pointer;
height: 14px;
width: 14px;
border: none;
background: black;
right: -7px;
top: 50%;
}
<div id="first_block" class="first_block">
<div id="auxiliary_block">
<div id="second_block" class="second_block"></div>
<input id="point" class="point" name="name_point" type="button">
</div>
</div>
You can work around this issue if the size of the change in the item is above a certain limit, or by checking the limit and stopping the update. I prevented the overflow caused by rapid mouse movement by updating its code as follows:
function change_second_block()
{
console.log("Event.PageX: " + event.pageX);
if(event.pageX < 500 )
{
if(second_block.clientWidth < 500)
{
second_block.style.width = `${start_x + event.pageX}px`;
}
}
}
References
Javascript mouse event not captured properly when mouse moved very fast
I have a custom cursor on my site that is working perfectly apart from one thing. When clicking through to a new page, when the page loads the cursor resets itself to the top left of the page regardless of where you leave the mouse on the page, then once you moved the mouse the cursor moves back to where the mouse is. I have tried removing "top" & "left" from the CSS but the problem remains. I cant see what is causing this to happen, and I just need the cursor to stay where the mouse is positioned on the page and not reset every time you navigate to a new page.
jQuery(document).ready(function($) {
let cursor = document.querySelector('#custom-cursor');
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent)) {
$('#custom-cursor').remove();
}
else { cursor.style.display = 'block';}
document.addEventListener('mousemove', evt => {
let { clientX: x, clientY: y } = evt;
let scale = 1;
if (evt.target.matches('a,span,[onclick],img,video,i')) {
cursor.classList.add('active');
scale = 0.5;
} else {
cursor.classList.remove('active');
}
cursor.style.transform = `translate(${x}px, ${y}px) scale(${scale})`;
});
});
* {
cursor: none;
}
#custom-cursor {
display: none;
position: fixed;
width: 20px; height: 20px;
top: -10px;
left: -10px;
border: 2px solid black;
border-radius: 50%;
opacity: 1;
background-color: #fb4d98;
pointer-events: none;
z-index: 99999999;
transition:
transform ease-out 0.15s,
border 0.5s,
opacity 0.5s,
background-color 0.5s;
}
#custom-cursor.active {
opacity: 0.5;
background-color: #000;
border: 2px solid #fb4d98;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="custom-cursor"></div>
Use ordinary CSS cursor as shown in the other answer and replace it with you fancy cursor in the first mouse event:
jQuery(document).ready(function($) {
let cursor = document.querySelector('#custom-cursor');
document.addEventListener('mousemove', evt => {
document.body.classList.add('custom-cursor-moved')
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent)) {
$('#custom-cursor').remove();
} else {
cursor.style.display = 'block';
}
let {
clientX: x,
clientY: y
} = evt;
let scale = 1;
if (evt.target.matches('a,span,[onclick],img,video,i')) {
cursor.classList.add('active');
scale = 0.5;
} else {
cursor.classList.remove('active');
}
cursor.style.transform = `translate(${x}px, ${y}px) scale(${scale})`;
});
});
body {
height: 100vh;
}
html,
body {
margin: 0;
padding: 0;
}
* {
cursor: url(https://i.stack.imgur.com/7pmmV.png) 0 0, auto;
}
.custom-cursor-moved,
.custom-cursor-moved * {
cursor: none !important;
}
#custom-cursor {
display: none;
position: fixed;
width: 20px;
height: 20px;
top: -10px;
left: -10px;
border: 2px solid black;
border-radius: 50%;
opacity: 1;
background-color: #fb4d98;
pointer-events: none;
z-index: 99999999;
transition: transform ease-out 0.15s, border 0.5s, opacity 0.5s, background-color 0.5s;
}
#custom-cursor.active {
opacity: 0.5;
background-color: #000;
border: 2px solid #fb4d98;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="custom-cursor"></div>
Try me.<br> Try me.
It needs a bit of modifications (better cursor image, fix it hotspot etc.) but it works.
Be very, very careful when doing such thing. Try to not break any accessibility tools and please do not assume that Android/some specific user-agent HAS touchscreen, etc.. Use proper APIs.
Use CSS cursor property instead:
html {
cursor: url(https://cdn.sstatic.net/Sites/stackoverflow/Img/favicon.ico?v=ec617d715196) 0 0, auto;
height: 100%;
}
Try me.
I am working with collision detection in vue.js using nuxt.js framework. I have done a similar program from this source
https://codepen.io/dropinks/pen/MrzPXB
I have converted this js code in vue friendly template and script. Only problem is that the the collision is not being detected . Please take a look at my code and tell where i made mistake.
Here is the link for codesandbox online editor where i have the code
https://codesandbox.io/s/wispy-hill-466s7?file=/pages/index.vue
template code:
<template>
<div class="container">
<div class="rectangle-1" id="rect">Hover Me</div>
<div class="rectangle-2" id="dragMe">Drag Me</div>
</div>
</template>
styles:
<style scoped>
container {
position: relative;
}
.rectangle-1 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: #4CAF50;
width: 180px;
height: 150px;
border-radius: 5px;
transition: 0.3s all ease;
color: #fff;
text-align: center;
line-height: 150px;
font-size: 25px;
}
.rectangle-1.collide {
background: #EF5350;
}
.rectangle-1:after {
content: ":-)";
position: absolute;
bottom: -50px;
left: 50%;
transform: translateX(-50%);
}
.rectangle-1.collide:after {
content: ":-(";
}
.rectangle-2 {
position: absolute;
background: #F5B041;
width: 100px;
height: 100px;
border-radius: 5px;
z-index: 10;
cursor: move;
transition: 0.5s box-shadow ease, 0.5s transform ease;
transform: translate(0, 0);
top: 40%;
left: 30%;
text-align: center;
line-height: 100px;
font-size: 17px;
}
.rectangle-2.onDrag {
box-shadow: 5px 5px 25px 0px rgba(0, 0, 0, 0.2);
transform: translate(-3px, -3px);
}
</style>
script
<script>
export default {
data() {
return {
dragMe: "",
rect: "",
};
},
created: function () {
if (process.client) {
this.myFunction();
}
},
methods: {
myFunction: function () {
this.rect = document.getElementById("rect");
this.dragMe = document.getElementById("dragMe");
this.initDrag({
element: this.dragMe,
drag: function () {
this.isCollapsed(this.dragMe, this.rect);
},
});
},
isCollapsed: function (dragMe, rect) {
var object_1 = this.dragMe.getBoundingClientRect();
var object_2 = this.rect.getBoundingClientRect();
if (
object_1.left < object_2.left + object_2.width &&
object_1.left + object_1.width > object_2.left &&
object_1.top < object_2.top + object_2.height &&
object_1.top + object_1.height > object_2.top
) {
rect.classList.add("collide");
document.getElementById('dragMe').style.background = 'blue';
} else {
rect.classList.remove("collide");
}
},
initDrag: function (options) {
var element = options.element;
var mousedown,
mouseup,
mousemove,
dragStart,
initX,
initY,
offsetLeft,
offsetTop;
function mouseMove(ev) {
if (dragStart) {
var newX = offsetLeft + (ev.pageX - initX);
var newY = offsetTop + (ev.pageY - initY);
element.style.top = newY + "px";
element.style.left = newX + "px";
options.drag.call();
}
}
function mouseUp(ev) {
dragStart = false;
document.removeEventListener("mousemove", mouseMove, false);
document.removeEventListener("mouseup", mouseUp, false);
options.stop.call();
}
function mouseDown(ev) {
initX = ev.pageX;
initY = ev.pageY;
dragStart = true;
offsetLeft = element.offsetLeft;
offsetTop = element.offsetTop;
document.addEventListener(
"mousemove",
function (ev) {
mouseMove(ev);
},
false
);
document.addEventListener(
"mouseup",
function (ev) {
mouseUp(ev);
},
false
);
options.start.call();
}
element.addEventListener(
"mousedown",
function (ev) {
mouseDown(ev);
},
false
);
},
},
};
</script>
I want to add 7 custom lines as helper to user.
Like in that picture, I want to add 7 times "div.moveable-line"
Even rotation change, the lines stayed at suitible position => And I want to add them 7 times.
Can we create a line between T1 and B1 (and for the others)?
Or if you have any other solutions, I am open for them as well.
React Moveable - Github
Warpable - StoryBook
Moveable.warpable - Documentation
Here is a demo link
MY COMPONENT
import React from 'react';
import ReactDOM from 'react-dom';
import Moveable from 'react-moveable';
import { ref } from 'framework-utils';
import { Frame } from 'scenejs';
import './styles.css';
class App extends React.Component {
frame = new Frame({
width: '250px',
height: '200px',
left: '0px',
top: '0px',
transform: {
rotate: '0deg',
scaleX: 1,
scaleY: 1,
matrix3d: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
},
});
state = {
target: null,
container: null,
warpable: true,
stateTransform: [],
totalBoxesTop: 0,
totalBoxesFill: 0,
totalBoxesBottom: 0,
isBoxCreated: false,
};
render() {
const { warpable, target } = this.state;
let k = document.querySelector('.moveable-control-box');
console.log(k, ':44');
if (k !== null) {
// k.appendChild(z);
// k.appendChild(d);
k.style.position = 'relative';
k.style.backgroundColor = '#fff';
}
let topLine = document.querySelector(
'.moveable-direction[data-line-index="0"]'
);
if (topLine !== null) {
topLine.classList.add('myTopLine');
let d = document.createElement('div'); // is a node
d.innerHTML = `T${this.state.totalBoxesTop}`;
d.setAttribute('data-box-position-top', `${this.state.totalBoxesTop}`);
d.classList.add('my-box');
if (this.state.totalBoxesTop < 8) {
// When is this.state.totalBoxes === 1 it means 0 boxes appear
topLine.appendChild(d);
this.setState({ totalBoxesTop: this.state.totalBoxesTop + 1 });
}
console.log(topLine, this.state.totalBoxesTop);
}
let bottomLine = document.querySelector(
'.moveable-direction[data-line-index="3"]'
);
if (bottomLine !== null) {
bottomLine.classList.add('myBottomLine');
let d = document.createElement('div'); // is a node
d.innerHTML = `B${this.state.totalBoxesBottom}`;
d.setAttribute(
'data-box-position-bottom',
`${this.state.totalBoxesBottom}`
);
d.classList.add('my-box');
if (this.state.totalBoxesBottom < 8) {
// When is this.state.totalBoxes === 1 it means 0 boxes appear
bottomLine.appendChild(d);
this.setState({ totalBoxesBottom: this.state.totalBoxesBottom + 1 });
}
console.log(bottomLine, this.state.totalBoxesBottom);
}
return (
<div className="page main">
<Moveable
ref={ref(this, 'moveable')}
target={target}
pinchThreshold={20}
container={document.body}
draggable={true}
warpable={warpable}
rotatable={true}
pinchable={true}
origin={false}
throttleDrag={1}
throttleRotate={0.2}
throttleResize={1}
throttleScale={0.01}
onDrag={this.onDrag}
onWarp={this.onWarp}
onDragEnd={this.onEnd}
onScaleEnd={this.onEnd}
onResizeEnd={this.onEnd}
onWarpEnd={this.onEnd}
onRotateEnd={this.onEnd}
onPinchEnd={this.onEnd}
/>
<div className="moveable">hello</div>
<div className="label" ref={ref(this, 'label')} />
</div>
);
}
componentDidMount() {
this.setState({
target: document.querySelector('.moveable'),
});
window.addEventListener('resize', this.onWindowReisze);
}
componentWillUnmount() {
window.removeEventListener('resize', this.onWindowReisze);
}
onWindowReisze = () => {
this.moveable.updateRect();
};
setTransform(target) {
target.style.cssText = this.frame.toCSS();
}
setLabel(clientX, clientY, text) {
this.label.style.cssText = `
display: block; transform: translate(${clientX}px, ${
clientY - 10
}px) translate(-100%, -100%) translateZ(-100px);`;
this.label.innerHTML = text;
}
onDrag = ({ target, clientX, clientY, top, left, isPinch }) => {
this.frame.set('left', `${left}px`);
this.frame.set('top', `${top}px`);
this.setTransform(target);
if (!isPinch) {
this.setLabel(clientX, clientY, `X: ${left}px<br/>Y: ${top}px`);
}
};
onWarp = ({
target,
clientX,
clientY,
delta,
multiply,
currentTarget,
moveable,
datas,
inputEvent,
transform,
dist,
matrix,
}) => {
console.log(target);
target.style.transform = `matrix3d(${matrix.join(',')})`;
this.setState({ stateTransform: `matrix3d(${matrix.join(',')})` });
this.frame.set(
'transform',
'matrix3d',
multiply(this.frame.get('transform', 'matrix3d'), delta)
);
this.setTransform(target);
this.setLabel(clientX, clientY, `X: ${clientX}px<br/>Y: ${clientY}px`);
};
onEnd = () => {
this.label.style.display = 'none';
};
}
export default App;
#import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,600&display=swap");
.moveable {
position: absolute;
width: 250px;
height: 200px;
margin: 0 auto;
background-color: transparent;
top: 0;
left: 0;
}
.my-new-box{
position: relative;
width: 100%;
height: 100%;
background-color: #73a079;
}
.myTopLine,
.myBottomLine{
background-color: #8b270a!important;
display: flex!important;
position: absolute!important;
justify-content: space-between!important;
align-items: flex-end!important;
}
.my-box {
position: relative;
top: 0;
left: 0;
width: 25px;
height: 25px;
/*flex: 1;*/
/*margin: 0 auto;*/
background-color: rgba(0,222,222,0.3);
/*transform: translate3d(42px, -62px, -135px);*/
}
.my-line{
position: relative;
top: 0;
left: 0;
width: 100px;
height: 150px;
background-color: #3a3aa0;
}
.moveable-control-box {
position: relative!important;
background-color: #8b2c62 !important;
}
.label {
position: fixed;
top: 0;
left: 0;
padding: 5px;
border-radius: 5px;
background: #333;
z-index: 3001;
color: #fff;
font-weight: bold;
font-size: 12px;
display: none;
transform: translate(-100%, -100%);
}
.feature .container .left {
position: relative;
width: 300px;
height: 205px;
display: inline-block;
vertical-align: top;
z-index: 2000;
margin-bottom: 20px;
}
.feature .container .right {
position: relative;
display: inline-block;
vertical-align: top;
flex: 1;
}
.feature .right .description {
text-align: left;
margin: 0px 0px 10px;
}
.feature .right .description strong {
font-weight: 600;
}
.draggable,
.resizable,
.scalable,
.rotatable,
.origin,
.warpable,
.pinchable {
position: absolute;
left: 0;
}
.origin {
transform-origin: 30% 50%;
}
pre {
position: relative;
border: 1px solid #ccc;
box-sizing: border-box;
padding: 10px;
max-width: 500px;
}
code.hljs {
padding: 0;
}
.tab {
padding: 10px 12px;
appearance: none;
-webkit-appearance: none;
background: transparent;
border: 1px solid #ccc;
box-shadow: none;
font-weight: bold;
margin: 0;
cursor: pointer;
outline: none;
}
.tab.selected {
background: #333;
color: #fff;
border: 1px solid #333;
}
.panel {
display: none;
}
.panel.selected {
display: block;
}
.page.footer {
font-weight: 400;
}
.page.footer a {
text-decoration: underline;
}
.page.footer span:first-child:before {
content: "";
}
.page.footer span:before {
content: "/";
}
Make Custom Able for Custom Lines
https://stackblitz.com/edit/react-ts-wmy77k?file=index.tsx
So I have a page where I have replaced my cursor with a div.
The cursor is simply a part of the page that I can animate using CSS.
The main thing I want to achieve is to make this cursor change size when I hover over any button.
I cannot get it to work...
Cursor positioning is handled by a JQuery script but the vanilla one doesn't seem like it wants to work with me...
I can can't fix the error...
// Jquery code that moves the cursor (div element)
$(document).on('mousemove', function(e){
$('#cursor').css({
left: e.pageX - 7,
top: e.pageY - 7
});
});
// Function to be executed when mouse is over a button
document.querySelectorAll('button').addEventListener("mouseover", cursorHovering);
function cursorHovering() {
document.getElementById('object').style = "transform: scale(2);";
}
body {
height: 300px;
width: 300px;
background-color: #ccc;
}
*, body { cursor: none !important; }
#cursor {
position: fixed;
z-index: 20000;
height: 15px;
width: 15px;
background-color: #ffffff;
mix-blend-mode: difference;
border-radius: 50%;
opacity: 0;
transition: 0.3s;
transition-property: transform, opacity;
pointer-events: none;
}
body:hover #cursor {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div id="cursor"></div>
<button class="button1">Hover over me (1)</button>
<button class="button2">Hover over me (2)</button>
<button class="button3">Hover over me (3)</button>
</body>
You mean something like this?
// Jquery code that moves the cursor (div element)
var c = document.getElementById('cursor');
document.addEventListener('mousemove', (e) => {
c.style.left = e.pageX - 7 + 'px';
c.style.top = e.pageY - 7 + 'px';
});
// Function to be executed when mouse is over a button
document
.querySelectorAll('button')
.forEach(b => {
b.addEventListener("mouseover", () => c.style.transform='scale(2)');
b.addEventListener("mouseout", () => c.style.transform='scale(1)');
});
body {
height: 300px;
width: 300px;
background-color: #ccc;
}
*, body { cursor: none !important; }
#cursor {
position: fixed;
z-index: 20000;
height: 15px;
width: 15px;
background-color: #ffffff;
mix-blend-mode: difference;
border-radius: 50%;
opacity: 0;
transition: 0.3s;
transition-property: transform, opacity;
pointer-events: none;
}
body:hover #cursor {
opacity: 1;
}
<body>
<div id="cursor"></div>
<button class="button1">Hover over me (1)</button>
<button class="button2">Hover over me (2)</button>
<button class="button3">Hover over me (3)</button>
</body>
Here's a vanilla JS solution.
document.addEventListener('mousemove', handleMouseMove, false);
// Cache the elements
const cursor = document.getElementById('cursor');
const buttons = document.querySelectorAll('button');
// For each button add the two event listeners
[...buttons].forEach(button => {
button.addEventListener('mouseover', handleMouseOver, false);
button.addEventListener('mouseout', handleMouseOut, false)
});
function handleMouseMove(e) {
// You need to ensure that you add "px" to the
// end of the value. jQuery does this automatically.
cursor.style.left = `${e.pageX - 7}px`;
cursor.style.top = `${e.pageY - 7}px`;
}
function handleMouseOver() {
cursor.style.transform = 'scale(2)';
}
function handleMouseOut() {
cursor.style.transform = 'scale(1)';
}
body {
height: 300px;
width: 300px;
background-color: #ccc;
}
*,
body {
cursor: none !important;
}
#cursor {
position: fixed;
z-index: 20000;
height: 15px;
width: 15px;
background-color: #ffffff;
mix-blend-mode: difference;
border-radius: 50%;
opacity: 0;
transition: 0.3s;
transition-property: transform, opacity;
pointer-events: none;
}
body:hover #cursor {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div id="cursor"></div>
<button class="button1">Hover over me (1)</button>
<button class="button2">Hover over me (2)</button>
<button class="button3">Hover over me (3)</button>
</body>