Skip to main content
Robb Nemo
Inspiring
January 6, 2025
Answered

generate multiple instances of a drag and drop symbol on demand

  • January 6, 2025
  • 4 replies
  • 1749 views

Hi, I've just been asked if I can produce an "Incident" map where various symbols of personnel and vehicles  can be dragged onto a map to demonstrate what is to be done in response to an emergency.

I have some fairly basic experience in Animate so the "drag and drop" functionality is manageable, but what I really need to be able to do is have multiple/indefinite numbers of each symbol available to the use, so when a symbol is dragged from a menu area and dropped onto the map, another version of that symbol pops up in its place on the menu, ready for dragging and dropping if required - effectively re-spawned.

Is this something that can be achieved and, if so, how would it be done?

    Correct answer JoãoCésar17023019

    Here is a sample.

    Preview:

    https://bit.ly/4j6NThy

     

    JavaScript code:

     

    const root = this;
    let spawnedItem;
    
    function main()
    {
    	createjs.Touch.enable(stage);
    	stage.mouseMoveOutside = true;
    	root.stop();
    	root.menu.children.forEach(child => child.mouseChildren = false);
    	root.menu.on("mousedown", menuDown);
    	root.menu.on("pressmove", menuMove);
    	root.menu.on("pressup", menuUp);
    	root.map.on("mousedown", mapDown);
    	root.map.on("pressmove", mapMove);
    }
    
    function menuDown(e)
    {	
    	spawnedItem = new e.target.constructor();
    	spawnedItem.x = e.currentTarget.x + e.target.x;
    	spawnedItem.y = e.currentTarget.y + e.target.y;
    	spawnedItem.mouseChildren = false;
    	root.addChild(spawnedItem);
    	setDrag(spawnedItem);
    }
    
    function menuMove()
    {
    	drag(spawnedItem);
    }
    
    function menuUp(e)
    {
    	var dropPoint = { x: spawnedItem.x, y: spawnedItem.y };
    
    	if (root.mapRec.hitTest(dropPoint.x - root.mapRec.x, dropPoint.y - root.mapRec.y))
    	{
    		root.map.addChild(spawnedItem);
    		spawnedItem.x = dropPoint.x - root.map.x;
    		spawnedItem.y = dropPoint.y - root.map.y;
    	}
    	else
    		root.removeChild(spawnedItem);
    	
    	spawnedItem = null;
    }
    
    function mapDown(e)
    {
    	setDrag(e.target);
    }
    
    function mapMove(e)
    {
    	drag(e.target, root.mapRec.nominalBounds);
    }
    
    function setDrag(target)
    {
    	target.offset =
    	{
    		x: stage.mouseX / stage.scaleX - target.x,
    		y: stage.mouseY / stage.scaleY - target.y
    	};
    }
    
    function drag(target, dragArea)
    {
    	target.x = stage.mouseX / stage.scaleX - target.offset.x;
    	target.y = stage.mouseY / stage.scaleY - target.offset.y;
    		
    	if (dragArea)
    	{
    		target.x = clamp(dragArea.x, target.x, dragArea.x + dragArea.width);
    		target.y = clamp(dragArea.y, target.y, dragArea.y + dragArea.height);
    	}
    }
    
    function clamp(min, value, max)
    {
    	if (value < min)
    		return min;
    	
    	if (value > max)
    		return max;
    	
    	return value;
    }
    
    main();

     

     

     

    Download / source / files:
    https://bit.ly/3W9A6wy

     

    Regards,

    JC

    4 replies

    JoãoCésar17023019
    Community Expert
    Community Expert
    January 15, 2025

    Hi again!

     

    Thank you very much for the compliments! And, yeah, they also add a whole new dimension to wrtiting code while we hold them in our arms! 😄

     

    Just added a v2. Hopefully it will address the remaining requested features.

     

    Preview:

    https://bit.ly/40gGNOO

     

    JavaScript code:

    const root = this;
    const SPACE_KEY = "Space";
    let keys, spawnedItem;
    
    function main()
    {
    	createjs.Touch.enable(stage);
    	stage.mouseMoveOutside = true;
    	keys = {};
    	root.stop();
    	root.map.minZoom = 1;
    	root.map.maxZoom = 3;
    	root.map.zoomStep = 0.05;
    	root.menu.children.forEach(child => child.mouseChildren = false);
    	root.menu.on("mousedown", menuDown);
    	root.menu.on("pressmove", menuMove);
    	root.menu.on("pressup", menuUp);
    	root.map.on("mousedown", mapItemDown);
    	root.map.on("pressmove", mapItemMove);
    	root.map.on("pressup", mapUp);
    	root.mapRec.on("mousedown", mapDown);
    	root.mapRec.on("pressmove", mapMove);
    	root.clearAllButton.on("click", clearMap);
    	window.addEventListener("keydown", onKeyDown);
    	window.addEventListener("keyup", onKeyUp);
    	canvas.addEventListener('mousewheel', onMouseWheel);
    	canvas.addEventListener('DOMMouseScroll', onMouseWheel);
    }
    
    function menuDown(e)
    {	
    	spawnedItem = new e.target.constructor();
    	spawnedItem.x = e.currentTarget.x + e.target.x;
    	spawnedItem.y = e.currentTarget.y + e.target.y;
    	spawnedItem.mouseChildren = false;
    	root.addChild(spawnedItem);
    	setDrag(spawnedItem);
    }
    
    function menuMove()
    {
    	drag(spawnedItem);
    }
    
    function menuUp(e)
    {
    	const dropPoint = { x: spawnedItem.x, y: spawnedItem.y };
    
    	if (root.mapRec.hitTest(dropPoint.x - root.mapRec.x, dropPoint.y - root.mapRec.y))
    	{
    		let localPos;
    		
    		root.map.addChild(spawnedItem);
    		localPos = root.map.globalToLocal(dropPoint.x * stage.scaleX, dropPoint.y * stage.scaleY);
    		spawnedItem.x = localPos.x;
    		spawnedItem.y = localPos.y;
    		spawnedItem.fromMenu = true;
    	}
    	else
    		root.removeChild(spawnedItem);
    	
    	spawnedItem = null;
    }
    
    function mapItemDown(e)
    {
    	if (keys[SPACE_KEY])
    		setDrag(e.currentTarget);
    	else
    	{
    		if (e.target.fromMenu)
    			setDrag(e.target);
    	}
    }
    
    function mapItemMove(e)
    {
    	if (keys[SPACE_KEY])
    		drag(e.currentTarget);
    	else
    	{
    		if (e.target.fromMenu)
    			drag(e.target);
    	}
    }
    
    function mapDown(e)
    {
    	if (keys[SPACE_KEY])
    		setDrag(root.map);
    }
    
    function mapMove(e)
    {
    	if (keys[SPACE_KEY])
    		drag(root.map);
    }
    
    function mapUp(e)
    {
    	const pos = e.target.localToLocal(0, 0, root.mapRec);
    	
    	if (!keys[SPACE_KEY] && !root.mapRec.hitTest(pos.x, pos.y))
    	{
    		e.target.parent.removeChild(e.target);
    		e.target = null;
    	}
    }
    
    function onKeyDown(e)
    {
    	keys[e.code] = true;
    }
    
    function onKeyUp(e)
    {
    	delete keys[e.code];
    }
    
    function onMouseWheel(e)
    {
    	const evt = window.event || e;
    	const mouseWheelDelta = clamp(-1, evt.wheelDelta || -evt.detail, 1);
    	const localCursorPosBefore = root.map.globalToLocal(stage.mouseX, stage.mouseY);
    	let localCursorPosAfter;
    	
    	root.map.regX = localCursorPosBefore.x;
    	root.map.regY = localCursorPosBefore.y;
    	localCursorPosAfter = root.map.globalToLocal(stage.mouseX, stage.mouseY);
    	root.map.x += (localCursorPosAfter.x - localCursorPosBefore.x) * root.map.scaleX;
    	root.map.y += (localCursorPosAfter.y - localCursorPosBefore.y) * root.map.scaleY;
    	root.map.scale = clamp(root.map.minZoom, root.map.scale + mouseWheelDelta * root.map.zoomStep, root.map.maxZoom);
    }
    
    function clearMap(e)
    {
    	let i, child;
    	
    	for (i = root.map.numChildren - 1; i > -1; i--)
    	{
    		child = root.map.children[i];
    		
    		if (child.fromMenu)
    		{
    			child.parent.removeChild(child);
    			child = null;
    		}
    	}
    }
    
    function setDrag(target)
    {
    	const pos = target.parent.globalToLocal(stage.mouseX, stage.mouseY);
    	
    	target.offset =
    	{
    		x: pos.x - target.x,
    		y: pos.y - target.y
    	};
    }
    
    function drag(target)
    {
    	const pos = target.parent.globalToLocal(stage.mouseX, stage.mouseY);
    		
    	target.x = pos.x - target.offset.x;
    	target.y = pos.y - target.offset.y;
    }
    
    function clamp(min, value, max)
    {
    	if (value < min)
    		return min;
    	
    	if (value > max)
    		return max;
    	
    	return value;
    }
    
    main();

     

    Download / source / files:
    https://bit.ly/3W9A6wy

     

    Robb Nemo
    Robb NemoAuthor
    Inspiring
    January 16, 2025

    JoãoCésar Thank you so much, the dag and drop works brilliantly. I notice the script looks like it uses touch gestures for the map zoom and drag. Although it will some times be used on a tablet, the map will be mainly used on desktops/laptops, and on something like a Prowise board for presentations, so is there any way that the zoom and drag functions can be added to buttons, similar to the way that Google maps work? 

    JoãoCésar17023019
    Community Expert
    JoãoCésar17023019Community ExpertCorrect answer
    Community Expert
    January 6, 2025

    Here is a sample.

    Preview:

    https://bit.ly/4j6NThy

     

    JavaScript code:

     

    const root = this;
    let spawnedItem;
    
    function main()
    {
    	createjs.Touch.enable(stage);
    	stage.mouseMoveOutside = true;
    	root.stop();
    	root.menu.children.forEach(child => child.mouseChildren = false);
    	root.menu.on("mousedown", menuDown);
    	root.menu.on("pressmove", menuMove);
    	root.menu.on("pressup", menuUp);
    	root.map.on("mousedown", mapDown);
    	root.map.on("pressmove", mapMove);
    }
    
    function menuDown(e)
    {	
    	spawnedItem = new e.target.constructor();
    	spawnedItem.x = e.currentTarget.x + e.target.x;
    	spawnedItem.y = e.currentTarget.y + e.target.y;
    	spawnedItem.mouseChildren = false;
    	root.addChild(spawnedItem);
    	setDrag(spawnedItem);
    }
    
    function menuMove()
    {
    	drag(spawnedItem);
    }
    
    function menuUp(e)
    {
    	var dropPoint = { x: spawnedItem.x, y: spawnedItem.y };
    
    	if (root.mapRec.hitTest(dropPoint.x - root.mapRec.x, dropPoint.y - root.mapRec.y))
    	{
    		root.map.addChild(spawnedItem);
    		spawnedItem.x = dropPoint.x - root.map.x;
    		spawnedItem.y = dropPoint.y - root.map.y;
    	}
    	else
    		root.removeChild(spawnedItem);
    	
    	spawnedItem = null;
    }
    
    function mapDown(e)
    {
    	setDrag(e.target);
    }
    
    function mapMove(e)
    {
    	drag(e.target, root.mapRec.nominalBounds);
    }
    
    function setDrag(target)
    {
    	target.offset =
    	{
    		x: stage.mouseX / stage.scaleX - target.x,
    		y: stage.mouseY / stage.scaleY - target.y
    	};
    }
    
    function drag(target, dragArea)
    {
    	target.x = stage.mouseX / stage.scaleX - target.offset.x;
    	target.y = stage.mouseY / stage.scaleY - target.offset.y;
    		
    	if (dragArea)
    	{
    		target.x = clamp(dragArea.x, target.x, dragArea.x + dragArea.width);
    		target.y = clamp(dragArea.y, target.y, dragArea.y + dragArea.height);
    	}
    }
    
    function clamp(min, value, max)
    {
    	if (value < min)
    		return min;
    	
    	if (value > max)
    		return max;
    	
    	return value;
    }
    
    main();

     

     

     

    Download / source / files:
    https://bit.ly/3W9A6wy

     

    Regards,

    JC

    Robb Nemo
    Robb NemoAuthor
    Inspiring
    January 7, 2025

    Hi JC,

    Thank you for that - I shall give it a try and let you know how I get on 🙂

    kglad
    Community Expert
    Community Expert
    January 6, 2025

    is this an html5 app?

    Robb Nemo
    Robb NemoAuthor
    Inspiring
    January 6, 2025

    Yes,  oops sorry... I forgot to mention that 

    kglad
    Community Expert
    Community Expert
    January 6, 2025

    assign a linkage to each library symbol (eg, S) you want the ability to add, and use

     

    var s = new lib.S();  // to create an instance

    this.addChild(s);  // to add the instance to the stage at 0,0

    JoãoCésar17023019
    Community Expert
    Community Expert
    January 6, 2025

    Hi.

     

    One approach would be to add a new symbol instance from Library (using addChild or addChildAt) on top of the selected menu item in a mouse down event handler and then add the drag and drob behavior to this new instance.

     

    Meaning that you'll add the same drag and drop behavior you already have but to a instance that has just been instantiated at runtime.

    Please let us know if you need further assistance.

     

    Regards,

    JC

    Robb Nemo
    Robb NemoAuthor
    Inspiring
    January 6, 2025

    Thank you for your assistance JC - and yes, with my limited experience, I probably will need further assistance! 🙂