Zimjs pressmove event doesn't work outer the elements - javascript

I'm using zimjs library for my canvas project. I'm trying to do continued pressing around the circles. When pressing and moving over the circles, circles must be change color. But pressmove event don't start outer the circles. You need to start to press over the circle. if I start pressing over the circle, the other circle is not affected.
Here is zimjs events:
https://zimjs.com/tips.html (See Events)
What do I want approximately?
https://zimjs.com/intro/index.html (See drag to eraser example. I want like this but without eraser. Only single pressing)
Note: I can't use mouse events. They didn't work on phone touchs.
See snippet:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>ZIM Frame - Outside Template</title>
<script src="https://zimjs.org/cdn/1.3.0/createjs.js"></script>
<script src="https://zimjs.org/cdn/10.9.0/zim.js"></script>
<!-- use zimjs.com/distill for minified individual functions! -->
<script>
var scaling = "fit"; // this will resize to fit inside the screen dimensions
var width = 600;
var height = 400;
var color = white; // or any HTML color such as "violet" or "#333333"
var outerColor = light;
var frame = new Frame(scaling, width, height, color, outerColor);
frame.on("ready", function() {
var stage = frame.stage;
var circle1 = new Circle(20, "red").pos(100,40, stage)
var circle2 = new Circle(20, "blue").pos(200,40, stage)
circle1.on("pressmove", () => {
circle1.color = "green"
stage.update()
console.log("Circle - 1 || Circles pressmove doesn't work when pressing start the outside")
})
circle2.on("pressmove", () => {
circle2.color = "green"
stage.update()
console.log("Circle - 2 || Circles pressmove doesn't work when pressing start the outside")
})
var button = new Button(140, 100, "reset", grey, dark).center(stage).tap(() => {
circle1.color = "red"
circle2.color = "blue"
stage.update()
})
new Label("Pressmove can be start anywhere", 16).pos(0, 300)
new Label("When pressing and moving over the circles,", 16).pos(0, 320)
new Label("circles must be select and change color,", 16).pos(0, 340)
stage.on("pressmove", () => console.log("Stage !!. Stage pressmove doesn't work when pressing outside the circles"))
stage.update();
}); // end of ready
</script>
<meta name="viewport" content="width=device-width, user-scalable=no" />
</head>
<body>
</body>
</html>

It is true that the pressmove event only works as you press on an object and move. If you want to start the press off the object or indeed anywhere then you use stage.on("stagemousedown", function); Mouse events DO work on mobile they are triggered by a touch so you do not even have to think of it. There is also "stagemousemove" and "stagemouseup". Objects on the stage get "mousedown", "presssmove", "pressup", "mouseover", "mouseout", etc. Usually we would change color on "mouseover" and change back on "mouseout" - ZIM has hov() that will do that. Or a Button() and Label() have built in backgroundColor and rollBackgroundColor.
If you really need to press anywhere before activating a change of color you could do something like this to remember the mousedown stage in general:
let mousedown = false;
stage.on("stagemousedown", ()=>{mousedown=true});
stage.on("stagemouseup", ()=>{mousedown=false});
const circle = new Circle().center();
circle.on("mouseover", ()=>{
if (mousedown) {
circle.color = red;
stage.update();
}
});
Or you could specifically add a stagemousemove event inside a stagemousedown event and remove it in a stagemouseup event to get the equivalent to a stage pressmove event. The pressmove event was created to help with dragging objects and usually, we do not drag the stage.
Hope this helps and apologies for the delay - you are welcome to join us at https://zimjs.com/slack for discussion! Cheers.

Related

Javascript / HTML adding an image to center position on the mouse click

I want to print out a picture whenever the mouse is clicked on the screen.
This is my current Javascript and HTML code. It only prints the h1, I don't know what I am doing wrong.
var image = document.getElementById("im"); // idenfitifes span eleme nt
var roll = false; // to know the image is moving or not , intitally it is not moving state hence value is false
image.addEventListener("mousedown", start, false); // at starting image is in rest
function mouse_move(x) // funciton to move image with mouse
{
var newX = x.clientX; // copies mouse x position
var newY = x.clientY; // copies mouse y position
image.style.left = newY + "px"; // assigns latest mouse position to span (image)
image.style.top = newX + "px";
}
function start(x) // span (image) starting no rolling mode
{
if(roll){ // when the mouse is moving
document.removeEventListener("mousemove", mouse_move); // initiages image span move
roll = !roll;
return;
}
roll = !roll; // when the mouse is not moving
document.addEventListener("mousemove", mouse_move, false);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title> Picture of Me! </title>
</head>
<body>
<h1> HTML File to move image along with mouse movement. </h1>
<script type="text/javascript" src="h5j.js">
<span id="im">
<img src="https://static01.nyt.com/images/2020/04/27/us/politics/00-trump-cand-page/00-trump-cand-page-mediumSquareAt3X.jpg" height="120" width="120"></img>
</span>
</body>
</html>
You Use The AddEventListener Function Like This
document.addEventListener("mousedown", mouse_move, false);
the syntax for addEventListener is:
addEventListener("Event Name", "Your Function name, the function you want to run after event happening")
when you need to run in the click event, you simply need to change Event name from "mousedown" to "click" , example:
document.addEventListener("click", mouse_move, false);
you have to use "click" event instead of "mousedown"
like:
document.addEventListener("click", mouse_move, false);
<code>
var image = document.getElementById("im");
var roll = false;
image.addEventListener("click", start, false);
function mouse_move(x)
{
var newX = x.clientX; // copies mouse x position
var newY = x.clientY; // copies mouse y position
image.style.left = newX + "px";
image.style.top = newY+ "px";
}
function start(x) // span (image) starting no rolling mode
{
if (roll) { // when the mouse is moving
document.removeEventListener("click", mouse_move); // initiages image span move
roll = !roll;
return;
}
roll = !roll; // when the mouse is not moving
}
document.addEventListener("click", mouse_move, false);
</code>
It looks like your trying to get the image element by id, before the DOM has finished loading.
I would suggest one of the following:
Move the <script> tag to the bottom of the page between </body> and </html>
Add defer to the <script> tag, which will cause the script to be loaded after the DOM.
Move the first block of your JavaScript into a DOMContentLoaded event listener as shown below.
var roll = false;
var image;
document.addEventListener('DOMContentLoaded', function(){
image = document.getElementById("im");
image.addEventListener("mousedown", start, false);
});

FabricJS - Better solution to centering object on cursor when selected?

I would like selectable objects to snap their center to my mouse cursor on click. The only modification allowed to the user in this case is moving the object, no scaling, rotating, etc. Simply updating the position of the object on mousedown or selected will update its position only until the moving event is fired, where the object will snap to its original position and then begin following the mouse.
rect.on('moving', moveHandler);
function moveHandler(evt) {
var mousePnt = $canvas.getPointer(evt.e);
rect.set({
left:mousePnt.x - rect.width*0.5 , top:mousePnt.y - rect.height*0.5});
this.setCoords();
}
This is what I've come up with to center a selectable rectangle on the cursor, but I'm certain it's firing two movement events. Is there a way to override that original positioning. Or should I instead write my own mousedown, mouseup, and moving listeners to mimic the default dragging behavior?
You solution is fine if no rotating or scaling is involved.
You are not executing anything more than necessary if not the .setCoords that during a movement is optional since it will be called on mouseUp when the translation is finished.
If you want to take the mouseDown approach, changing the position once for all, you can use this logic:
var canvas = new fabric.Canvas('c');
canvas.add(new fabric.Rect({width: 50, height: 50}));
canvas.on('mouse:down', function(opt) {
if (this._currentTransform && opt.target) {
var target = opt.target;
target.top = this._currentTransform.ey - target.height/2;
target.left = this._currentTransform.ex - target.width/2;
this._currentTransform.left = target.left;
this._currentTransform.offsetX = this._currentTransform.ex - target.left;
this._currentTransform.offsetY = this._currentTransform.ey - target.top;
this._currentTransform.top = target.top;
this._currentTransform.original.left = target.left;
this._currentTransform.original.top = target.top;
this.renderAll();
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.18/fabric.min.js"></script>
<canvas id="c" ></canvas>
You have to modify all the transform information that fabric noted down on the mouseDown event before the your mousedown handler is executed.

Chart.js drag points on linear chart

I have a simple linear chart built with Chart.js library.
And i want to allow user to drag points on chart for dynamically change data of it. I tied chartjs-plugin-draggable but it works for me only with annotations. I need graph exactly like this:
https://www.rgraph.net/canvas/docs/adjusting-line.html
But use new graph library in project is not good solution :(
Also i tried to play with dot event's.
UPDATE:
With angular i created something like this.
Maybe if there is no way to add drag&drop to points, there will be a hack to put "sliders" with absolute position on graph on points positions. I didn't find any info too :(
In case anyone is looking for a solution that doesn't require the use of plugins, it's pretty straightforward to do it in vanilla chart.js.
Here's a simple working example - just click and drag a data point
// some data to be plotted
var x_data = [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050];
var y_data_1 = [86,114,106,106,107,111,133,221,783,2478];
var y_data_2 = [2000,700,200,100,100,100,100,50,25,0];
// globals
var activePoint = null;
var canvas = null;
// draw a line chart on the canvas context
window.onload = function () {
// Draw a line chart with two data sets
var ctx = document.getElementById("canvas").getContext("2d");
canvas = document.getElementById("canvas");
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data_1,
label: "Data 1",
borderColor: "#3e95cd",
fill: false
},
{
data: y_data_2,
label: "Data 2",
borderColor: "#cd953e",
fill: false
}
]
},
options: {
animation: {
duration: 0
},
tooltips: {
mode: 'nearest'
}
}
});
// set pointer event handlers for canvas element
canvas.onpointerdown = down_handler;
canvas.onpointerup = up_handler;
canvas.onpointermove = null;
};
function down_handler(event) {
// check for data point near event location
const points = window.myChart.getElementAtEvent(event, {intersect: false});
if (points.length > 0) {
// grab nearest point, start dragging
activePoint = points[0];
canvas.onpointermove = move_handler;
};
};
function up_handler(event) {
// release grabbed point, stop dragging
activePoint = null;
canvas.onpointermove = null;
};
function move_handler(event)
{
// locate grabbed point in chart data
if (activePoint != null) {
var data = activePoint._chart.data;
var datasetIndex = activePoint._datasetIndex;
// read mouse position
const helpers = Chart.helpers;
var position = helpers.getRelativePosition(event, myChart);
// convert mouse position to chart y axis value
var chartArea = window.myChart.chartArea;
var yAxis = window.myChart.scales["y-axis-0"];
var yValue = map(position.y, chartArea.bottom, chartArea.top, yAxis.min, yAxis.max);
// update y value of active data point
data.datasets[datasetIndex].data[activePoint._index] = yValue;
window.myChart.update();
};
};
// map value to other coordinate system
function map(value, start1, stop1, start2, stop2) {
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1))
};
body {
font-family: Helvetica Neue, Arial, sans-serif;
text-align: center;
}
.wrapper {
max-width: 800px;
margin: 50px auto;
}
h1 {
font-weight: 200;
font-size: 3em;
margin: 0 0 0.1em 0;
}
h2 {
font-weight: 200;
font-size: 0.9em;
margin: 0 0 50px;
color: #555;
}
a {
margin-top: 50px;
display: block;
color: #3e95cd;
}
<!DOCTYPE html>
<html>
<!-- HEAD element: load the stylesheet and the chart.js library -->
<head>
<title>Draggable Points</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<!-- BODY element: create a canvas and render a chart on it -->
<body>
<!-- canvas element in a container -->
<div class="wrapper">
<canvas id="canvas" width="1600" height="900"></canvas>
</div>
<!-- call external script to create and render a chart on the canvas -->
<script src="script.js"></script>
</body>
</html>
Update: My previous answer got deleted because it only featured a link to a plugin solving the issue, however here comes the explanation to what it does:
The general procedure on how to achieve the desired behaviour is to
Intercept a mousedown (and check if it's a dragging gesture) on a given chart
Check if the mousedown was over a data point using the getElementAtEvent function
On mousemove, translate the new Y-Pixel value into a data coordinate using the axis.getValueForPixel function
Synchronously update the chart data using chart.update(0)
as pointed out in this Chart.js issue.
In order to intercept the mousedown, mousemove and mouseup events (the dragging gesture), event listeners for said events need to be created. In order to simplify the creation of the listeners one may use the d3 library in this case as follows:
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', getElement)
.on('drag', updateData)
.on('end', callback)
);
On mousedown (the 'start' event here), a function (getElement) may be called thatfetches the closest chart element to the pointers location and gets the ID of the Y-Scale
function getElement () {
var e = d3.event.sourceEvent
element = chartInstance.getElementAtEvent(e)[0]
scale = element['_yScale'].id
}
On mousemove ('drag'), the chart data is supposed to be updated according to the current Y-Pixel value of the pointer. We can therefore create an updateData function that gets the position of the clicked data point in the charts data array and the according dataset like this
function updateData () {
var e = d3.event.sourceEvent
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.scales[scale].getValueForPixel(e.clientY)
chartInstance.data.datasets[datasetIndex].data[index] = value
chartInstance.update(0)
}
And that's it! If you need to store the resulting value after dragging, you may also specify a callback function like this
function callback () {
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.data.datasets[datasetIndex].data[index]
// e.g. store value in database
}
Here is a working fiddle of the above code. The functionality is also the core of the Chart.js Plugin dragData, which may be easier to implement in many cases.
Here is how I fixed using both touchscreen or mouse event x,y coordinates for the excellent d3 example above by wrapping event screen coordinates in a more "generic" x,y object.
(Probably d3 has something similar to handle both types of events but lot of reading to find out..)
//Get an class of {points: [{x, y},], type: event.type} clicked or touched
function getEventPoints(event)
{
var retval = {point: [], type: event.type};
//Get x,y of mouse point or touch event
if (event.type.startsWith("touch")) {
//Return x,y of one or more touches
//Note 'changedTouches' has missing iterators and can not be iterated with forEach
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches.item(i);
retval.point.push({ x: touch.clientX, y: touch.clientY })
}
}
else if (event.type.startsWith("mouse")) {
//Return x,y of mouse event
retval.point.push({ x: event.layerX, y: event.layerY })
}
return retval;
}
.. and here is how I would use it in the above d3 example to store the initial grab point Y. And works for both mouse and touch.
Check the Fiddle
Here how I solved the problem with using d3 and wanting to drag the document on mobile or touch screens. Somehow with the d3 event subscription all Chart area events where already blocked from bubbling up the DOM.
Was not able to figure out if d3 could be configured to pass canvas events on without touching them. So in a protest I just eliminated d3 as it was not much involved other than subscribing events.
Not being a Javascript master this is some fun code that subscribes the events the old way. To prevent chart touches from dragging the screen only when a chart point is grabed each of the handlers just have to return true and the event.preventDefault() is called to keep the event to your self.
//ChartJs event handler attaching events to chart canvas
const chartEventHandler = {
//Call init with a ChartJs Chart instance to apply mouse and touch events to its canvas.
init(chartInstance) {
//Event handler for event types subscribed
var evtHandler =
function myeventHandler(evt) {
var cancel = false;
switch (evt.type) {
case "mousedown":
case "touchstart":
cancel = beginDrag(evt);
break;
case "mousemove":
case "touchmove":
cancel = duringDrag(evt);
break;
case "mouseup":
case "touchend":
cancel = endDrag(evt);
break;
default:
//handleDefault(evt);
}
if (cancel) {
//Prevent the event e from bubbling up the DOM
if (evt.cancelable) {
if (evt.preventDefault) {
evt.preventDefault();
}
if (evt.cancelBubble != null) {
evt.cancelBubble = true;
}
}
}
};
//Events to subscribe to
var events = ['mousedown', 'touchstart', 'mousemove', 'touchmove', 'mouseup', 'touchend'];
//Subscribe events
events.forEach(function (evtName) {
chartInstance.canvas.addEventListener(evtName, evtHandler);
});
}
};
The handler above is initiated like this with an existing Chart.js object:
chartEventHandler.init(chartAcTune);
The beginDrag(evt), duringDrag(evt) and endDrag(evt) have the same basic function as in the d3 example above. Just returns true when wanting to consume the event and not pasing it on for document panning and similar.
Try it in this Fiddle using a touch screen. Unless you touch close to select a chart point the rest of the chart will be transparent to touch/mouse events and allow panning the page.

How to program javascript radio buttons

I have this piece of code that will eventually be a little drawing tool in which the user will be able switch between a pencil and an eraser. I am trying to program the eraser at the moment. However, if the user selects the eraser radio button and then selects the pencil radio button(deselects the eraser), the eraser does not 'turn off'.
This is all done with HTML's canvas element and the eraser takes advantage of:
context.globalCompositeOperation="destination-out";
As the code below shows, I thought I might be able to go through and reverse some of the properties depending on which button was clicked, for instance changing the globalCompositeOperation to equal 'source-over' or another property of globalCompositeOperation, however as far as I know, once a property has been set for it the only way to change it is to reset the entire canvas, which would delete everything.
Then I thought I might be able to set the opacity of the brush using:
context.globalAlpha=0;
so that when the pencil tool is selected the eraser tool still erases, but at 0% opacity, so it shouldn't do anything. Unfortunatly, none of this worked.
I'm self taught so although I know a moderate amount about the HTML canvas, I've found it difficult to learn about the JS side of programming radio buttons.
I guess my question is, if some code is activated through a radiobutton onclick event, how do I deactivate the code once the radiobutton is deselected.
Thanks to any suggestions, any help is wanted!
Here's my code:
(sorry about the formatting, stack overflow didn't like my indents)
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="main">
<div class="tools" style="position:fixed;">Pencil:<input type="radio" name="toolSelect" onclick="pencilCheck();" id="pencilSelect"></div>
<div class="tools" style="position:fixed; left: 80px;">Rubber:<input type="radio" name="toolSelect" onclick="rubberCheck()" id="rubberSelect"></div>
<canvas id="canvas">This is a web application, you need to update your browser.</canvas><!-- the canvas -->
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
//Makes the canvas the size of the browser window
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context.fillStyle = "red";
context.fillRect(40,40,250,100);
function rubberCheck(){
var dragging = false;
var erase = function(rubber){
if(dragging){
context.beginPath();
context.arc(rubber.clientX, rubber.clientY, 10, 0, Math.PI*2);
context.fill();
context.globalCompositeOperation="destination-out";
context.globalAlpha=1;
}
}
var click = function(rubber){
dragging = true;
erase(rubber);
}
var unclick = function(){
dragging = false;
}
canvas.addEventListener('mousedown', click);
canvas.addEventListener('mousemove', erase);
canvas.addEventListener('mouseup', unclick);
}
if(pencilCheck){
var dragging = false;
var erase = function(rubber){
if(dragging){
context.beginPath();
context.arc(rubber.clientX, rubber.clientY, 10, 0, Math.PI*2);
context.fill();
context.globalCompositeOperation="destination-over";
context.globalAlpha=0;
}
}
var click = function(rubber){
dragging = true;
erase(rubber);
}
var unclick = function(){
dragging = false;
}
canvas.addEventListener('mousedown', click);
canvas.addEventListener('mousemove', erase);
canvas.addEventListener('mouseup', unclick);
}
</script>
</body>
You have:
> <input type="radio" name="toolSelect" onclick="pencilCheck();" id="pencilSelect">
but there is no pencilCheck function. Later there is:
> if (pencilCheck) {
The function should probably be called "setTool" or similar. Pass a reference to the button that was clicked to the function using this and add a value attribute that is the tool that the button represents:
<input type="radio" ... value="pencil" onclick="setTool(this);" ...>
Then set the value of a variable to indicate the selected tool, say selectedTool:
// Initial state is no tool selected (?)
var selectedTool = null;
function setTool(el) {
if (el.checked) selectedTool = el.value;
}
Do the same with other radio buttons so selectedTool always represents the currently checked checkbox. Note that you have a reset button, you need to add a listener so when it's clicked you reset the value of selectedTool (that can use a click listener on the button or a reset listener on the form, if you're using a form, but you don't seem to be using one).

Easel.js: Browser compatibility when placing an image within a container and adding mouse interactions

I'm going to start this question off by saying that this is 100% working in Firefox (v21.0). For some reason it's not working in Google Chrome (v27.0.1453.94m). It also doesn't work in IE10.
Here is the JavaScript code I'm having issues with:
function canvasDrawBackground(value){
console.log(value);
stage.removeChild(background);
var temp = new createjs.Bitmap("images/bg_" + value +".jpg");
background = new createjs.Container();
background.x = background.y = 0;
background.addChild(temp);
stage.addChild(background);
background.addEventListener("mousedown", function(evt) {
var offset = {x:evt.target.x-evt.stageX, y:evt.target.y-evt.stageY};
evt.addEventListener("mousemove",function(ev) {
ev.target.x = ev.stageX+offset.x;
ev.target.y = ev.stageY+offset.y;
stage.update();
});
});
stage.update();
}
So, in Firefox the above code works, as in the image is added to the canvas and you can drag it around.
In Chrome / IE10 nothing happens - or more simply nothing appears on the canvas. I think the issue is in regards to when I add the image into the container, as I can place other items into the container and it works.
I am using http://code.createjs.com/easeljs-0.6.1.min.js and this code is based off of the "drag" tutorial. Here's the code from the tutorial:
<!DOCTYPE html>
<html>
<head>
<title>EaselJS demo: Dragging</title>
<link href="../../shared/demo.css" rel="stylesheet" type="text/css">
<script src="http://code.createjs.com/easeljs-0.6.0.min.js"></script>
<script>
var stage, output;
function init() {
stage = new createjs.Stage("demoCanvas");
// this lets our drag continue to track the mouse even when it leaves the canvas:
// play with commenting this out to see the difference.
stage.mouseMoveOutside = true;
var circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0, 0, 50);
var label = new createjs.Text("drag me", "bold 14px Arial", "#FFFFFF");
label.textAlign = "center";
label.y = -7;
var dragger = new createjs.Container();
dragger.x = dragger.y = 100;
dragger.addChild(circle, label);
stage.addChild(dragger);
dragger.addEventListener("mousedown", function(evt) {
var offset = {x:evt.target.x-evt.stageX, y:evt.target.y-evt.stageY};
// add a handler to the event object's onMouseMove callback
// this will be active until the user releases the mouse button:
evt.addEventListener("mousemove",function(ev) {
ev.target.x = ev.stageX+offset.x;
ev.target.y = ev.stageY+offset.y;
stage.update();
});
});
stage.update();
}
</script>
</head>
<body onLoad="init();">
<canvas id="demoCanvas" width="500" height="200">
alternate content
</canvas>
</body>
</html>
To simulate my issue, change "var circle = new createjs.Shape();" into a bitmap / image, createjs.Bitmap("images/bg_" + value +".jpg");. It then doesn't render.
Any help is greatly appreciated! Hopefully I'm just doing it wrong. :P
This is probably because the image is not loaded. If you only update the stage after creating it, the image may not display. I would recommend adding a callback to the image to update the stage after its loaded.
// Simple approach. May not work depending on the scope of the stage.
var temp = new createjs.Bitmap("images/bg_" + value +".jpg");
temp.image.onload = function() { stage.update(); }
It also may make sense to preload the images you intend to use.

Categories

Resources