matter-js icon indicating copy to clipboard operation
matter-js copied to clipboard

How to drag multiple items simultaneously?

Open m90khan opened this issue 1 year ago • 2 comments

How to add multi drag support such that two or more items can be drag simultaneously?

It is going to be a multi-touch/drag application allowing users to drag multiple items at the same time.

Example: I have boxA and boxB which are connected to HTML div elements. I want to add multi-drag support such that boxA and boxB can be drag simultaneously.

I saw two other posts which are dead end.

Any lead would be very helpful. Thank you.

` import Head from 'next/head'; import { Box, Button, Flex, Heading, Image, Text } from '@chakra-ui/react'; import React, { Fragment, useEffect, useRef, useState } from 'react'; import Matter from 'matter-js'; import MatterAttractors from 'matter-attractors';

import { Colors } from '@lib/helpers';

import Draggable from 'react-draggable';

export default function InteractiveDisplay() { // Attractor const executiveAttractRef = useRef([]); const judicativeAttractRef = useRef([]);

// DOM Refs

const engineRef = useRef(); const worldRef = useRef(); const canvasRef = useRef(); const requestRef = useRef(); const judicativeBoxRef = useRef(); const executiveBoxRef = useRef();

const resetBodiesRef = useRef();

let domRenders = [];

const animate = () => { Matter.use(MatterAttractors); // Matter.use(MatterDomPlugin);

// Canvas setup
const canvas = document.getElementById('world');
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;

window.addEventListener('resize', function () {
  canvas.width = document.documentElement.clientWidth;
  canvas.height = document.documentElement.clientHeight;
  location.reload();
});

function percentX(percent) {
  return Math.round((percent / 100) * canvas.width);
}
function percentY(percent) {
  return Math.round((percent / 100) * canvas.height);
}

const Engine = Matter.Engine,
  Render = Matter.Render,
  Runner = Matter.Runner,
  Events = Matter.Events,
  MouseConstraint = Matter.MouseConstraint,
  Mouse = Matter.Mouse,
  Composite = Matter.Composite,
  Constraint = Matter.Constraint,
  Composites = Matter.Composites,
  Bodies = Matter.Bodies,
  Body = Matter.Body,
  World = Matter.World;

// create engine
engineRef.current = Engine.create();
const engine = engineRef.current,
  // const engine = Engine.create(),
  world = engine.world;
engine.gravity.y = 0;
// create renderer
worldRef.current = world;
const render = Render.create({
  element: canvasRef.current,
  engine: engine,
  options: {
    width: percentX(100),
    height: percentY(100),
    wireframes: false,
    background: Colors.transparent,
  },
});

const runner = Runner.create();
Runner.run(runner, engine);
Render.run(render);

const bodiesSize = 600;
const colliderState = {
  Size: bodiesSize,
  X: canvas.width / 2 - bodiesSize / 2,
  Y: canvas.height / 2 - bodiesSize / 2,
};


const defaultState = {
  mainBodies: {
    radius: 115, //150 default
    judicative: {
      x: colliderState.X + colliderState.Size / 2,
      y: colliderState.Y,
    },
    executive: {
      x: colliderState.X + colliderState.Size,
      y: colliderState.Y + colliderState.Size / 2,
    },
  },
  colliderState,
};
const boxA = {
  body: Bodies.circle(
    defaultState.mainBodies.judicative.x,
    defaultState.mainBodies.judicative.y + 300,
    defaultState.mainBodies.radius / 2,
    {
      isSensor: false,
      label: `judicative`,
      isStatic: false,

      render: {
        strokeStyle: Colors.transparent,
        fillStyle: 'green',
        lineWidth: 1,
      },
      plugin: {
        attractors: [
          function (bodyA, bodyB) {
            const label = bodyB.label.split(' ');
            if (judicativeAttractRef?.current && label[1] === 'judicative') {
              const element = judicativeAttractRef?.current.filter(
                (el) => el.label === label[2]
              );
              if (element.length > 0 && label[2] === element[0].label) {
                return {
                  x: (bodyA.position.x - bodyB.position.x) * 0.0005,
                  y: (bodyA.position.y - bodyB.position.y) * 0.0005,
                };
              }
            }
          },
        ],
      },
    }
  ),
  elem: judicativeBoxRef.current,
  render() {
    const { x, y } = this.body.position;
    this.elem.style.top = `${y - 10}px`;
    this.elem.style.left = `${x + 60}px`;
  },
};

resetBodiesRef.current = {
  ...resetBodiesRef.current,
  boxA,
};
domRenders.push(boxA);

const boxB = {
  body: Matter.Bodies.circle(
    defaultState.mainBodies.executive.x,
    defaultState.mainBodies.executive.y,
    defaultState.mainBodies.radius / 2,
    {
      isSensor: false,
      label: `executive`,
      isStatic: false,

      render: {
        strokeStyle: Colors.transparent,
        fillStyle: 'blue',
        lineWidth: 1,
      },
      plugin: {
        attractors: [
          function (bodyA, bodyB) {
            const label = bodyB.label.split(' ');
            if (executiveAttractRef?.current && label[1] === 'executive') {
              const element = executiveAttractRef?.current.filter(
                (el) => el.label === label[2]
              );
              if (element.length > 0 && label[2] === element[0].label) {
                return {
                  x: (bodyA.position.x - bodyB.position.x) * 0.0005,
                  y: (bodyA.position.y - bodyB.position.y) * 0.0005,
                };
              }
            }
          },
        ],
      },
    }
  ),
  elem: executiveBoxRef.current,
  render() {
    const { x, y } = this.body.position;
    this.elem.style.top = `${y - 10}px`;
    this.elem.style.left = `${x + 60}px`;
  },
};
resetBodiesRef.current = {
  ...resetBodiesRef.current,
  boxB,
};
domRenders.push(boxB);

const mainParts = [boxA.body, boxB.body];

World.add(world, mainParts);
const mouse = Mouse.create(render.canvas),
  mouseConstraint = MouseConstraint.create(engine, {
    // mouse: mouse,
    element: canvasRef.current,
    constraint: {
      stiffness: 0.2,
      render: {
        visible: false,
      },
    },
  });

Composite.add(world, mouseConstraint);
render.mouse = mouse;

 Render.lookAt(render, {
  min: { x: 0, y: 0 },
  max: { x: canvas.width, y: canvas.height },
});

(function rerender() {
  domRenders.length > 0 && domRenders.map((box) => box.render());
  Matter.Engine.update(engine);
  requestRef.current = requestAnimationFrame(rerender);
})();

};

useEffect(() => { animate(); return () => { cancelAnimationFrame(requestRef.current); Matter.Engine.clear(engineRef.current); }; }, []);

return ( <Flex w={'100vw'} h={'100vh'} ref={canvasRef} id={'world'} pos={'relative'}> <Box ref={judicativeBoxRef} height={{ md: '15rem', '2xl': '30rem' }} width={{ md: '15rem', '2xl': '30rem' }} position={'absolute'} display={'flex'} justifyContent={'center'} alignItems={'center'} className={'juris'} zIndex={100} borderRadius={'50rem'} transformOrigin={'center center'} willChange={'left,top'} > <Image src={'/images/center/juris-de.svg'} pos={'absolute'} userSelect={'none'} pointerEvents={'none'} height={{ md: '60%', '2xl': '85%' }} width={{ md: '60%', '2xl': '85%' }} /> </Box>

  <Box
    ref={executiveBoxRef}
    height={{ md: '15rem', '2xl': '30rem' }}
    width={{ md: '15rem', '2xl': '30rem' }}
    position={'absolute'}
    display={'flex'}
    justifyContent={'center'}
    alignItems={'center'}
    className={'exec'}
    zIndex={100}
    borderRadius={'50rem'}
    transformOrigin={'center center'}
    //   bg={'rgba(35, 62, 214, 0.2)'}
  >
    <Image
      src={'/images/center/executive-en.svg'}
      pos={'absolute'}
      userSelect={'none'}
      pointerEvents={'none'}
      height={{ md: '60%', '2xl': '85%' }}
      width={{ md: '60%', '2xl': '85%' }}
    />
  </Box>
</Flex>

); } `

m90khan avatar Jun 02 '23 20:06 m90khan

Hi @liabru, Thanks for your amazing work. Sorry for directly tagging you here. I came across this requirement after completely finishing the project. i have been trying to fix this for over a week now. Do you know any leads on the issue? Thank you for your time.

m90khan avatar Jun 02 '23 20:06 m90khan

touch-events Maybe it can help you

MLH-AIDS avatar Jun 03 '23 06:06 MLH-AIDS