I am making a web game (not with webGL, but with html elements) and I need to move character with WASD keys.
I tried these things:
Used event listeners keydown and keyup.
Problem is that it is unresponsive and doesnt work really well when multiple keys are pressed simultaneously
Used setInterval(20ms) and event listeners.
On my stronger laptop everything works fine, but I feel like it is using insane amount of cpu power because my laptop starts sounding like a plane. On weaker laptop it wasn't working as well as first one, it was choppy and laggy
keyDict = {}
document.addEventListener('keydown', key => keyDict[key.code] = true);
document.addEventListener('keyup', key => keyDict[key.code] = false);
moveID = setInterval(move, 20)
function move()
{
if(!finished)
{
newDirs = [0,0]
//Left
if(keyDict.ArrowLeft == true)
{
newDirs[0] -= 1;
}
//Right
if(keyDict.ArrowRight == true)
{
newDirs[0] += 1;
}
//Up
if(keyDict.ArrowUp == true)
{
newDirs[1] -= 1;
}
//Down
if(keyDict.ArrowDown == true)
{
newDirs[1] += 1;
}
map.updateDir(newDirs);
}
}
Used requestAnimationFrame and event listeners.
On stronger laptop it looks like it utilizes 144 fps and its even more smoother, but sometimes it doesn't even respond to my controls. My laptop still sounds as it is working too hard
keyDict = {}
document.addEventListener('keydown', key => keyDict[key.code] = true);
document.addEventListener('keyup', key => keyDict[key.code] = false);
requestAnimationsFrame(move)
function move()
{
same code...
requestAnimationFrame(move)
}
I want to make it responsive and very smooth and I know there is way but don't know how. Example of this is mouse, your laptop doesn't get worked up from scrolling (and, for example, moving google maps with mouse is smooth and doesn't use cpu as much).
Don't use the 20ms interval.
Move the player inside the requestAnimationFrame depending on which key Event.code is pressed and holds a truthy value inside of your keyDict object:
const keyDict = {};
const Player = {
el: document.querySelector("#player"),
x: 200,
y: 100,
speed: 2,
move() {
this.el.style.transform = `translate(${this.x}px, ${this.y}px)`;
}
};
const updateKeyDict = (ev) => {
const k = ev.code;
if (/^Arrow\w+/.test(k)) { // If is arrow
ev.preventDefault();
keyDict[k] = ev.type === "keydown"; // set boolean true / false
}
};
const update = () => {
// Determine move distance to account diagonal move: 1/Math.sqrt(2) = ~0.707
let dist =
keyDict.ArrowUp && (keyDict.ArrowLeft || keyDict.ArrowRight) ||
keyDict.ArrowDown && (keyDict.ArrowLeft || keyDict.ArrowRight) ? 0.707 : 1;
dist *= Player.speed;
if (keyDict.ArrowLeft) Player.x -= dist;
if (keyDict.ArrowUp) Player.y -= dist;
if (keyDict.ArrowRight) Player.x += dist;
if (keyDict.ArrowDown) Player.y += dist;
Player.move();
}
document.addEventListener('keydown', updateKeyDict);
document.addEventListener('keyup', updateKeyDict);
(function engine() {
update();
window.requestAnimationFrame(engine);
}());
#player {
position: absolute;
left: 0;
top: 0;
width: 20px;
height: 20px;
background: #000;
border-radius: 50%;
}
Click here to focus, and use arrows
<div id="player"></div>
The above example uses Event.code for Arrows, which gives "ArrowLeft/Up/Right/Down" but you can change it accordingly to use "KeyW/A/S/D" instead.
"WASD" keys example
const keyDict = {};
const Player = {
el: document.querySelector("#player"),
x: 200,
y: 100,
speed: 2,
move() {
this.el.style.transform = `translate(${this.x}px, ${this.y}px)`;
}
};
const updateKeyDict = (ev) => {
const k = ev.code;
if (/^Key[WASD]/.test(k)) { // If is "KeyW,A,S,D" key
ev.preventDefault();
keyDict[k] = ev.type === "keydown"; // set boolean true / false
}
};
const update = () => {
// Determine move distance to account diagonal move: 1/Math.sqrt(2) = ~0.707
let dist =
keyDict.KeyW && (keyDict.KeyA || keyDict.KeyD) ||
keyDict.KeyS && (keyDict.KeyA || keyDict.KeyD) ? 0.707 : 1;
dist *= Player.speed;
if (keyDict.KeyA) Player.x -= dist;
if (keyDict.KeyW) Player.y -= dist;
if (keyDict.KeyD) Player.x += dist;
if (keyDict.KeyS) Player.y += dist;
Player.move();
}
document.addEventListener('keydown', updateKeyDict);
document.addEventListener('keyup', updateKeyDict);
(function engine() {
update();
window.requestAnimationFrame(engine);
}());
#player {
position: absolute;
left: 0;
top: 0;
width: 20px;
height: 20px;
background: #000;
border-radius: 50%;
}
Click here to focus, and use keys WASD
<div id="player"></div>
I am creating an editor where nodes (dojo groups) are connected to each other via links (dojo lines). When moving these nodes using applyTransform(matrix) the node moves correctly but the entire line moves according to the transformation. What I want is for only the point connected to the node to move with it and for the other point to stay where it is.
I have a custom mover which should move the node and the connected line point
dojo.declare("customMover", dojox.gfx.Mover, {
onMouseMove: function(event) {
let transform = this.shape.getTransform();
if (transform==null) {
transform = { dx: 0, dy: 0 };
}
let x = event.clientX;
let y = event.clientY;
this.shape.applyLeftTransform({
dx: x - this.lastX,
dy: y - this.lastY
});
this.shape.bonds.forEach(bond => {
console.log(bond)
for (let nodeIndex = 0; nodeIndex < bond.nodes.length; nodeIndex++) {
let node = bond.nodes[nodeIndex];
if (node === this.shape) {
// do something to change 1 point of the line
}
}
})
this.lastX = x;
this.lastY = y;
dojo.stopEvent(event);
}
})
and a function that creates lines between two nodes.
export function createLine(start, end, group) {
let startMat = null;
let endMat = null;
let startNode = start.getParent()
let endNode = end.getParent()
if (startNode.matrix) {
startMat = startNode.matrix
}
if (endNode.matrix) {
endMat = endNode.matrix
}
let line = group.createLine({
x1: startMat ? start.shape.cx + startMat.dx : start.shape.cx,
y1: startMat ? start.shape.cy + startMat.dy : start.shape.cy,
x2: endMat ? end.shape.cx + endMat.dx : end.shape.cx,
y2: endMat ? end.shape.cy + endMat.dy : end.shape.cy})
line.moveToBack()
line.setStroke({})
line.nodes = [startNode, endNode]
startNode.bonds.push(line)
endNode.bonds.push(line)
}
How could I change one point of the line?
I am using react with typescript. I use SVG to draw rectangles inside it. I am working on two features, the first one is I have to draw any number of shapes inside the SVG, and the other one is to allow mouse drag option. Now, the problem is when even I am drawing a shape and then drawing another shape in it the first drawn shape is moving and the new shape is drawing.
I want to do if I click and move the shape my drawing rectangle functionality should not work and if I am drawing the rectangle the already drawn shape would not move. this happening because I am using mouseup and mousemove events for both logic and that is why they collapsing. I don't know to separate them.
here is my code:
import "./styles.css";
import React, { useEffect, useRef } from 'react';
interface Iboxes{
id: string,
coordinates:{
x: number
y: number
width: number
height: number
}
}
export default function App() {
const divRef = useRef<HTMLDivElement>(null);
const svgRef = useRef<SVGSVGElement>(null);
const boxes: Array<Iboxes> = [];
useEffect(() => {
const containerSvg = svgRef.current;
let p:DOMPoint;
let w:number;
let h:number;
if(containerSvg){
const svgPoint = (elem:any, x:number, y:number) => {
const point = containerSvg.createSVGPoint();
point.x = x;
point.y = y;
return point.matrixTransform(elem.getScreenCTM().inverse());
};
containerSvg.addEventListener('mousedown', (event) => {
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
const start = svgPoint(containerSvg, event.clientX, event.clientY);
const drawRect = (e:any) => {
p = svgPoint(containerSvg, e.clientX, e.clientY);
w = Math.abs(p.x - start.x);
h = Math.abs(p.y - start.y);
if (p.x > start.x) {
p.x = start.x;
}
if (p.y > start.y) {
p.y = start.y;
}
rect.setAttributeNS(null, 'x', p.x as unknown as string);
rect.setAttributeNS(null, 'y', p.y as unknown as string);
rect.setAttributeNS(null, 'width', w as unknown as string);
rect.setAttributeNS(null, 'height', h as unknown as string);
rect.setAttributeNS(null, 'class', 'svg_rec');
rect.setAttributeNS(null, 'id', 'rec_' + boxes.length as unknown as string)
containerSvg.appendChild(rect);
};
const endDraw = (e:any) => {
containerSvg.removeEventListener('mousemove', drawRect);
containerSvg.removeEventListener('mouseup', endDraw);
boxes.push({id:'rec_' + boxes.length as unknown as string, coordinates:{x: p.x, y: p.y, width: w, height: h}})
let offset:any;
let selectedRect: SVGRectElement | null;
rect.addEventListener('mousedown', startDrag);
rect.addEventListener('mousemove', drag);
rect.addEventListener('mouseup', endDrag);
rect.addEventListener('mouseleave', endDrag);
function startDrag(evt:any) {
selectedRect = rect;
if(selectedRect){
offset = getMousePosition(evt);
if(offset){
let rectX = selectedRect.getAttributeNS(null, 'x');
let rectY = selectedRect.getAttributeNS(null, 'y');
if(rectX && rectY){
offset.x -= parseFloat(rectX);
offset.y -= parseFloat(rectY);
}
}
}
}
function drag(evt:any) {
if(selectedRect){
var coord = getMousePosition(evt);
if(coord && offset){
let x = coord.x - offset.x;
let y = coord.y - offset.y;
if(x && y){
selectedRect.setAttributeNS(null, "x", x as unknown as string);
selectedRect.setAttributeNS(null, "y", y as unknown as string);
}
}
}
}
function endDrag() {
selectedRect = null;
}
function getMousePosition(evt:any) {
var CTM = rect.getScreenCTM();
if(CTM){
return {
x: (evt.clientX - CTM.e) / CTM.a,
y: (evt.clientY - CTM.f) / CTM.d
};
}
}
};
containerSvg.addEventListener('mousemove', drawRect);
containerSvg.addEventListener('mouseup', endDraw);
});
}
},[]);
return (
<div className="App">
<div className='container' ref={divRef}>
<svg id="svg" ref={svgRef}>
</svg>
</div>
</div>
);
}
I also created a sandbox environment to demonstrate my issue:
here is a sandbox link
You just need to add
evt.stopPropagation();
to the function startDrag() on line 79.
function startDrag(evt: any) {
evt.stopPropagation();
selectedRect = rect;
// ...
}
Here's the fixed sandbox
Edit
Because other rects on top may obscure the current dragging rect. One solution is to disable the pointer-events for other rects while dragging one rect:
Add following code to the startDrag function
containerSvg.classList.add("dragging");
selectedRect?.classList.add("target");
and then add following code to the endDrag function
containerSvg.classList.remove("dragging");
selectedRect?.classList.remove("target");
then in your css code, add following rule:
.dragging rect:not(.target) {
pointer-events: none;
}
Checked updated sandbox
so ive been testing out HTML canvas. im trying to get a sprite to change on keyboard input.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas>
<script>
window.onload = function(){
var Game = document.getElementById('Game');
var context = Game.getContext('2d')
var room = new Image();
var lx = 0;
var ly = 0;
var li = 0;
var lo = 0;
var lwidth = 100;
var lheight = 100;
room.onload = function(){
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
}
room.src = 'https://i.ibb.co/D7fL7yN/Room.png';
var sprite = new Image();
var cx = 0;
var cy = 125;
var sy = 0;
var sx = 0;
var swidth = 35;
var sheight = 34;
sprite.onload = function(){
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
}
sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png';
}
</script>
</body>
</html>
ive been searching on how to change the SX with Keyboard input so my character changes sprites. can you help me? a code example would be best!
Tracking keyboard state.
You can create an object that hold the state of the keyboard, specifically the keys you are interested in reacting to. Use the "keydown" and "keyup" KeyboardEvent to update the keyboard state as the events fire. Use the KeyboardEvent property code to workout which key is changing. DO NOT use keyCode as that has depreciated and is Non Standard
You also want to prevent the default behaviour of keys. Eg prevent arrow keys scrolling the page. This is done by calling the event preventDefault function
const keys = {
ArrowRight: false,
ArrowLeft: false,
ArrowUp: false,
ArrowDown: false,
}
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
if (keys[event.code] !== undefined) {
keys[event.code] = event.type === "keydown";
event.preventDefault();
}
}
Then in the game you need only check the keyboard state
if (keys.ArrowRight) { moveRight() }
if (keys.ArrowLeft) { moveLeft() }
// and so on
In the demo below the keys are binded to game actions, meaning that what and how many keys are used are independent of the action. The are also set via configuration, so that key binding can be changed without changing game code. You can also bind other inputs as in example
Animation
To animate you should use the timer function requestAnimationFrame as it is specifically designed to give the best animation results. It will call your rendering function, you can consider the rendering function like a loop, that is call every time you step forward in animation time.
Putting it together
The demo below use the above (modified) methods to get keyboard input and render the scene via animation frame requests.
It also uses some techniques (simple versions of) that help make your game a better product.
Encapsulates the player as an object
Maintains game state by holding the current rendering function in currentRenderState
Has configuration config so all important values are in one place, and could be loaded (from JSON file) to easily change the game without changing code.
Has configurable keyboard binding, Note more than one key can be bound to a game action. In example movement is via WASD or arrow keys.
All text is configurable (making it language independent)
Passes the 2D context to all rendering code.
Separates the game from the rendering. This makes it easier to port the game to low end or high end devices or even move it to a server where ctx is replaced with coms and the game can be broadcast . The game does not change only how it is rendered
var currentRenderState = getFocus; // current game state
const config = {
player: {
start: {x: 100, y:100},
speed: 2,
imageURL: "https://i.stack.imgur.com/C7qq2.png?s=64&g=1",
},
keys: { // link key code to game action
up: ["ArrowUp", "KeyW"],
down: ["ArrowDown", "KeyS"],
left: ["ArrowLeft", "KeyA"],
right: ["ArrowRight", "KeyD"],
},
touchableTime: 140, // in ms. Set to 0 or remove to deactivate
text: {
focus: "Click canvas to get focus",
loading: "Just a moment still loading media!",
instruct: "Use arrow keys or WASD to move",
}
};
requestAnimationFrame(mainLoop); // request first frame
const ctx = gameCanvas.getContext("2d");
const w = gameCanvas.width, h = gameCanvas.height;
const player = {
image: (()=> {
const img = new Image;
img.src = config.player.imageURL;
img.addEventListener("load", () => player.size = img.width, {once: true});
return img;
})(),
x: config.player.start.x,
y: config.player.start.y,
size: 0,
speed: config.player.speed,
direction: 0,
update() {
var oldX = this.x, oldY = this.y;
if (actions.left) { this.x -= this.speed }
if (actions.right) { this.x += this.speed }
if (actions.up) { this.y -= this.speed }
if (actions.down) { this.y += this.speed }
if (this.x < 0) { this.x = 0 }
else if (this.x > w - this.size) { this.x = w - this.size }
if (this.y < 0) { this.y = 0 }
else if (this.y > h - this.size) { this.y = h - this.size }
const mx = this.x - oldX, my = this.y - oldY;
if (mx !== 0 || my !== 0) { this.direction = Math.atan2(my, mx) }
},
draw(ctx) {
if (ctx) {
ctx.setTransform(1, 0, 0, 1, this.x + this.size / 2, this.y + this.size / 2);
ctx.rotate(this.direction + Math.PI / 2); // rotate 90 deg as image points up
ctx.drawImage(this.image,-this.size / 2, -this.size / 2, this.size, this.size);
}
}
}
function drawText(ctx, text, size, color) {
if (ctx) {
ctx.fillStyle = color;
ctx.font = size + "px Arial";
ctx.textAlign = "center";
ctx.fillText(text, w / 2, h * (1/4));
}
}
function getFocus(ctx) {
drawText(ctx, config.text.focus, 24, "black");
}
function drawScene(ctx) {
if (!player.size === 0) {
drawText(ctx, config.text.loading, 16, "blue")
actions.hasInput = false; // ensure instruction are up when ready
} else {
if (!actions.hasInput) { drawText(ctx, config.text.instruct, 16, "blue") }
player.update();
player.draw(ctx);
}
}
function mainLoop() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, w, h);
currentRenderState(ctx);
requestAnimationFrame(mainLoop); // request next frame
}
// keys holds action name for each named key. eg for up action ArrowUp: "up", KeyW: "up",
const keys = Object.entries(config.keys)
.reduce((keys, [action,codes]) =>
codes.reduce((keys, code) => (keys[code] = action, keys), keys), {});
// actions are set true when key down. NOTE first up key for action cancels action
const actions = Object.keys(config.keys)
.reduce((actions,action) => (actions[action] = false, actions),{});
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
if (keys[event.code] !== undefined) {
actions[keys[event.code]] = event.type === "keydown";
event.preventDefault();
actions.hasInput = true;
}
}
if (config.touchableTime) {
const actionTimers = {};
touchable.addEventListener("click", (e) => {
if (e.target.dataset.action) {
actions[e.target.dataset.action] = true;
clearTimeout(actionTimers[e.target.dataset.action]);
actionTimers[e.target.dataset.action] = setTimeout(() => actions[e.target.dataset.action] = false, config.touchableTime);
actions.hasInput=true;
if (currentRenderState !== drawScene) {
window.focus();
currentRenderState = drawScene;
}
}
});
} else {
touchable.classList.add("hide");
}
gameCanvas.addEventListener("click", () => currentRenderState = drawScene, {once: true});
canvas {border: 1px solid black}
#game {
width:402px;
height:182px;
font-size: 24px;
user-select: none;
}
.left {
position: absolute;
top: 160px;
left: 10px;
cursor: pointer;
}
.right {
position: absolute;
top: 160px;
left: 355px;
cursor: pointer;
}
#touchable span:hover {color: red}
.hide { display: none }
<div id="game">
<canvas id="gameCanvas" width="400" height="180"></canvas>
<div id="touchable">
<div class="left">
<span data-action="up">▲</span>
<span data-action="down">▼</span>
</div>
<div class="right">
<span data-action="left">◄</span>
<span data-action="right">►</span>
</div>
</div>
</div>
Click to snippet frame area for focusing keyboard events
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas>
<script>
window.onload = function(){
// Keyboard collect
const keys = [];
document.onkeydown = e => {
var code = e.which;
if(keys.indexOf(code) < 0){
keys.push(code);
}
};
document.onkeyup = e => keys.splice(keys.indexOf(e.which),1);
// constants
const Game = document.getElementById('Game');
const context = Game.getContext('2d')
const room = new Image();
const lx = 0;
const ly = 0;
const li = 0;
const lo = 0;
const lwidth = 100;
const lheight = 100;
room.onload = function(){
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
}
room.src = 'https://i.ibb.co/D7fL7yN/Room.png';
const sprite = new Image();
const swidth = 35;
const sheight = 34;
const sy = 0;
sprite.onload = function(){
context.drawImage(sprite,0,sy,swidth,sheight,0,cy,50,50);
}
sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png';
// variables
let cx = 0;
let cy = 125;
let sx = 0;
// new variables
const frames_per_step = 20;
let moving = false; // moving flag
let step = 0; // frame counter (for steps)
// main loop function
function tick() {
// keyboard process
if (keys.length) {
keys.forEach(item => {
switch(item){
case 68:case 39://D and right arrow
cx += 1; // move right
// change sprite
if (step++ < frames_per_step / 2) {
sx = 35; // leg up
} else {
sx = 70; // leg down
if(step > frames_per_step) step = 0;
}
moving = true;
break;
case 65:case 37://A and left arrow
cx -= 1; // move left
// change sprite
if (step++ < frames_per_step / 2) {
sx = 105;
} else {
sx = 140;
if(step > frames_per_step) step = 0;
}
moving = true;
break;
// no sprite mechanics here, just move
case 87:case 38://W adn arrow up
cy -= 1;
break;
case 83:case 40://S adn arrow down
cy += 1;
break;
}
});
// render
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
} else if(moving) { // return sprite to stay position
sx = 0;
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
moving = false;
} // else do nothing
requestAnimationFrame(tick);
}
tick();
}
</script>
</body>
</html>
I am trying to move a div with the help of mousemove event.
Here is the code for the same.
https://codepen.io/anurag92/pen/VEoQOZ
class ImageMarker extends React.Component {
constructor(props) {
super(props);
this.mouseDown = this.mouseDown.bind(this);
this.mouseUp = this.mouseUp.bind(this);
this.mouseMove = this.mouseMove.bind(this);
this.paint = this.paint.bind(this);
}
mouseDown(e) {
const position = {
left: this.marker.offsetLeft,
top: this.marker.offsetTop
};
this.hitOffset = {
x: e.pageX - position.left,
y: e.pageY - position.top,
diameter: this.diameter(),
markerRadius: 10
};
this.marker.addEventListener('mousemove', this.mouseMove);
this.marker.addEventListener('mouseup', this.mouseUp);
this.marker.addEventListener('mouseleave', this.mouseUp);
e.preventDefault();
}
mouseMove(e) {
this.position = {
x: e.pageX - this.hitOffset.x,
y: e.pageY - this.hitOffset.y
};
this.position.x = Math.round(this.position.x);
this.position.y = Math.round(this.position.y);
this.position.x = Math.min(700 - 1, Math.max(0, this.position.x));
this.position.y = Math.min(700 - 1, Math.max(0, this.position.y));
this.paint();
}
mouseUp(e) {
this.marker.removeEventListener('mousemove', this.mouseMove);
this.marker.removeEventListener('mouseup', this.mouseUp);
this.marker.removeEventListener('mouseleave', this.mouseUp);
}
diameter() {
return 1;
}
paint() {
if (JSON.stringify(this.paintedPosition) !== JSON.stringify(this.position)) {
this.paintedPosition = Object.assign({}, this.position);
}
if (this.position) {
this.marker.style.left = `${100 * this.position.x / 700}%`;
this.marker.style.top = `${100 * this.position.y / 700}%`;
}
return this;
}
render() {
this.position = this.position || {
x: 5,
y: 5
};
this.offset = 0;
return <div className='outer'
ref = {ref => {
this.canvasRef = ref;
}}
>
<div className = 'marker'
onMouseDown = {event => this.mouseDown(event)}
ref = {ref => {
this.marker = ref;
}} >
</div>
</div>;
}
}
// export default ImageMarker;
ReactDOM.render(<ImageMarker />,
document.getElementById('root')
);
When i move my cursor slowly its working fine, but on fast movement mouseleave gets triggered and as a result div is not able to keep up with the cursor.
Can someone please tell me a potential fix for this.
You could resolve that by attaching the mouseMove (and mouseUp)to the whole document This way they will be fired no matter if the mouse gets out of the element you want to drag.
Just remember to detach the event during componentWillUnmount to avoid leaks.
Further more if you want you site to work on mobile you need to attach touch, pointer or drag events. See the code of the kendo-draggable abstraction for a reference. We are using it in our react components. Bind to the element on ComponentDidMount and detach on ComponentWillUnmount