I have a custom built fabric.js bundle with touch support. Now I can scale any object with the pinch-zoom gesture. The problem is the zoom is really really sensitive, I barely move my fingers and the object is hugely scaled.
I couldn't find much information in the documents about how I can change the sensitivity. I know Event.js is used to handle the touch events within fabric.js. Is there any way I can change this sensitivity?
Ok, I ended up implementing touch controls myself, this is the code I made. This code was placed on the added event of my custom fabric.js object.
////////////////////////////// Touch event handlers
// Add listener event for pinch-zoom
var bbScope = this;
var hammer = new Hammer.Manager(this.canvas.upperCanvasEl);
var pinch = new Hammer.Pinch();
hammer.add([pinch]);
hammer.on('pinch', function (ev) {
// Set the scale and render only if we have a valid pinch (inside the object)
if (bbScope._validPinch) {
bbScope.set('scaleX', ev.scale);
bbScope.set('scaleY', ev.scale);
bbScope.canvas.renderAll();
}
});
hammer.on('pinchend', function (ev) {
bbScope._validPinch = false;
});
hammer.on('pinchcancel', function (ev) {
bbScope._validPinch = false;
});
hammer.on('pinchstart', function (ev) {
// Convert mouse coordinates to canvas coordinates
ev.clientX = ev.center.x;
ev.clientY = ev.center.y;
// Check if the pinch was started inside this object
if (bbScope.canvas) {
var p = bbScope.canvas.getPointer(ev);
bbScope._validPinch = bbScope.containsPoint(p);
}
else {
bbScope._validPinch = false;
}
});
Related
Is there any difference between writing JS touch events for iPad vs. iPhone? I have read a ton of documentation and as far as I can tell it should work the same way for both.
I have a drag-and-drop game, basically you grab a coin from under the dragon and drag it over to your vault. The dragging works on iPad, but not on iPhone. I'm trying to figure out why.
The game, for reference: https://codeeverydamnday.com/projects/dragondrop/dragondrop.html
The JS, abridged to just the relevant code for this question (with comments for clarity):
var dragndrop = (function() {
var myX = "";
var myY = "";
// The coin's starting X and Y coordinate positions
var coin = "";
// The coin you start touching / dragging
function touchStart(e) {
e.preventDefault();
// Prevents default behavior of scrolling when you touch/drag on mobile
var coin = e.target;
var touch = e.touches[0];
var moveOffsetX = coin.offsetLeft - touch.pageX;
var moveOffsetY = coin.offsetTop - touch.pageY;
// Defines offset between left edge of coin and where you place your finger on it
coin.addEventListener('touchmove', function() {
var positionX = touch.pageX+moveOffsetX;
var positionY = touch.pageY+moveOffsetY;
// Defines the X-Y coordinates of wherever you stop dragging
coin.style.left = positionX + 'px';
coin.style.top = positionY + 'px';
// Updates the coin's X-Y coordinates with the new positions
}, false)
}
document.querySelector('body').addEventListener('touchstart', touchStart, false);
})();
If it helps, I am getting this console log error every time I click / tap on the iPad screen in the Chrome Dev Tools emulator:
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
This error doesn't seem to prevent the dragging from working on iPad, but I'm not sure if it has anything to do with the dragging not working on mobile? I tried adding a few things to fix the error based on other Stack Overflow questions I saw (ex. adding touch-action: none; in my CSS, but the error persisted).
Anyone see anything wrong in my code? I would love to get this game playable on mobile, as that's how most people would access it!
The default value of the passive option is set to true for touch-start and touch-move events, and it being true means your function won't call preventDefault to disable scrolling.
Simply set the passive value to false to solve your issue.
var dragndrop = (function() {
var myX;
var myY;
var coin;
function touchStart(e) {
e.preventDefault();
coin = e.target;
const touch = e.touches[0];
const moveOffsetX = whichArt.offsetLeft - touch.pageX;
const moveOffsetY = whichArt.offsetTop - touch.pageY;
coin.addEventListener("touchmove", touchMove, { passive: false });
function touchMove(e) {
const touch = e.touches[0];
const positionX = touch.pageX + moveOffsetX;
const positionY = touch.pageY + moveOffsetY;
coin.style.left = `${positionX}px`;
coin.style.top = `${positionY}px`;
}
}
document.body.addEventListener('touchstart', touchStart, { passive: false });
})();
Edit
I looked at the code from the website you linked, and I realized that one reason the coin wasn't dragged was because of touch we were using and also because of the once option I passed to the touchmove event.
Whenever the touchmove event is used, we have to use the new touch to get the pageX and pageY positions on the screen, I decided to create a function for the touchmove event, because whenever the touchstart function is called, a new touchmove event is registered because of the anonymous function handler.
So creating and naming a function for it will prevent the same function from being added.
I am trying to set up a script to draw lines on dynamically added canvases, so there could be any number of canvases on the page. This is the script for the code so far, but it's not showing the lines or drawing when clicked on. I think the issue is in the drawLine function. The canvases will be added via a second script which also adds other elements.
<script type="text/javascript">
// Variables to keep track of the mouse position and left-button status
var mouseX,mouseY,mouseDown=0;
// Variables to keep track of the touch position
var touchX,touchY;
function drawLine(ctx,x,y,size) {
// If lastX is not set, set lastX and lastY to the current position
if (lastX==-1) {
lastX=x;
lastY=y;
}
// Let's use black by setting RGB values to 0, and 255 alpha (completely opaque)
r=0; g=0; b=0; a=255;
// Select a fill style
ctx.strokeStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
// Set the line "cap" style to round, so lines at different angles can join into each other
ctx.lineCap = "round";
//ctx.lineJoin = "round";
// Draw a filled line
ctx.beginPath();
// First, move to the old (previous) position
ctx.moveTo(lastX,lastY);
// Now draw a line to the current touch/pointer position
ctx.lineTo(x,y);
// Set the line thickness and draw the line
ctx.lineWidth = size;
ctx.stroke();
ctx.closePath();
// Update the last position to reference the current position
lastX=x;
lastY=y;
}
// Get the current mouse position relative to the top-left of the canvas
function getMousePos(e) {
if (!e)
var e = event;
if (e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
}
else if (e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
}
// Get the touch position relative to the top-left of the canvas
// When we get the raw values of pageX and pageY below, they take into account the scrolling on the page
// but not the position relative to our target div. We'll adjust them using "target.offsetLeft" and
// "target.offsetTop" to get the correct values in relation to the top left of the canvas.
function getTouchPos(e) {
if (!e)
var e = event;
if(e.touches) {
if (e.touches.length == 1) { // Only deal with one finger
var touch = e.touches[0]; // Get the information for finger #1
touchX=touch.pageX-touch.target.offsetLeft;
touchY=touch.pageY-touch.target.offsetTop;
}
}
}
// Keep track of the mouse button being released
function sketchpad_mouseUp() {
mouseDown=0;
}
// Set-up an individual sketchpad and add our event handlers
// Use the "this" reference to make sure variables and functions are specific to each
// specific sketchpad created in the "init()" function below.
function sketchpad(my_sketchpad) {
// Get the specific canvas element from the HTML document passed
this.canvas = document.getElementById(my_sketchpad);
// If the browser supports the canvas tag, get the 2d drawing context for this canvas,
// and also store it with the canvas as "ctx" for convenience
if (this.canvas.getContext)
this.ctx = this.canvas.getContext('2d');
// Declare some functions associated with a particular sketchpad
// We will attach these to the canvas as event handlers later
// Note that the sketcphad_mouseUp function is not included here, since it's not
// specific to a certain canvas - we're listening to the entire window for mouseup
// events.
// Clear the canvas context using the canvas width and height
this.clearCanvas = function() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
// Draw something when a touch start is detected
this.sketchpad_touchStart = function() {
// Update the touch co-ordinates
getTouchPos();
drawLine(this.ctx,touchX,touchY,12);
// Prevents an additional mousedown event being triggered
event.preventDefault();
}
// Draw something and prevent the default scrolling when touch movement is detected
this.sketchpad_touchMove = function(e) {
// Update the touch co-ordinates
getTouchPos(e);
// During a touchmove event, unlike a mousemove event, we don't need to check if the touch is engaged, since there will always be contact with the screen by definition.
drawLine(this.ctx,touchX,touchY,12);
// Prevent a scrolling action as a result of this touchmove triggering.
event.preventDefault();
}
// Keep track of the mouse button being pressed and draw a dot at current location
this.sketchpad_mouseDown = function() {
mouseDown=1;
drawLine(this.ctx,mouseX,mouseY,12);
}
// Keep track of the mouse position and draw a dot if mouse button is currently pressed
this.sketchpad_mouseMove = function(e) {
// Update the mouse co-ordinates when moved
getMousePos(e);
// Draw a dot if the mouse button is currently being pressed
if (mouseDown==1) {
drawLine(this.ctx,mouseX,mouseY,12);
}
}
// Add event handlers
// Check that we have a valid context to draw on/with before adding event handlers
if (this.ctx) {
// React to mouse events on the canvas, and mouseup on the entire document
this.canvas.addEventListener('mousedown', this.sketchpad_mouseDown.bind(this), false);
this.canvas.addEventListener('mousemove', this.sketchpad_mouseMove.bind(this), false);
// React to touch events on the canvas
this.canvas.addEventListener('touchstart', this.sketchpad_touchStart.bind(this), false);
this.canvas.addEventListener('touchmove', this.sketchpad_touchMove.bind(this), false);
}
}
// Create two sketchpads when the page loads, using our canvas elements called sketchpad1 and sketchpad2
function init() {
sketch1 = new sketchpad('sketchpad1');
//sketch2 = new sketchpad('sketchpad2');
// Since we are listening to the entire window for the mouseup, it only needs to be done once per page,
// and not once per canvas
window.addEventListener('mouseup', sketchpad_mouseUp, false);
}
Your troubles have nothing to do with the how the canvases are added.
var touchX,touchY;
Should be:
var touchX,touchY,lastX=-1,lastY=-1;
Inside an object (created based on this tutorial), I have the following code. In this code, the lines:
event.target.x = evt.stageX;
event.target.y = evt.stageY;
are wrong. What should they be to access the mouse movement?:
(function() {
....
var p = createjs.extend(Card, createjs.Container);
p.setup = function() {
this.on("pressmove", this.handlePressMove);
....
p.handlePressMove = function (event) {
event.target.x = evt.stageX; //"Event" is wrong. So is "evt"
event.target.y = evt.stageY;
stage.setChildIndex(this, stage.getNumChildren()-1);
stage.update();
}
}
}());
Your code sample uses evt.stageX instead of event.stageX. All MouseEvents will have a stageX and stageY, which is the position the mouse was in when it fired the event. I think your code came from this tutorial which uses evt exlusively.
Additionally, MouseEvents have a rawX and rawY on pressMove events, which give you the x/y outside of the canvas element. There is no clientX or clientY on EaselJS MouseEvents.
Here is a quick sample using the stageX/stageY.
http://jsfiddle.net/lannymcnie/suva8vt3/
Snippet:
shape.on("pressmove", function(event) {
shape.x = event.stageX;
shape.y = event.stageY;
});
I'm working on adding some interactivity to some buttons in an HTML5 canvas document in FLASH CC. The functionality works great, but I can't figure out how to add the JS to it for adding a rollover so that the handcursor/pointer appears when the buttons are rolled over.
I know how to do this in AS3 through the buttonMode, but I'm a total noob to JS.
Below is the code that I have currently for the buttons in the HTMl5 canvas doc, Thanks in advance for any help!
var frequency = 1;
stage.enableMouseOver(frequency);
this.btn_yes.addEventListener("click", clickYes.bind(this));
this.btn_no.addEventListener("click", clickNo.bind(this));
this.btn_yes.addEventListener("mouseover", MouseOverYes);
this.btn_yes.addEventListener("mouseout", MouseOutYes);
this.btn_no.addEventListener("mouseover", MouseOverNo);
this.btn_no.addEventListener("mouseout", MouseOutNo);
function clickYes()
{
this.gotoAndPlay("chooseYes");
}
function clickNo()
{
this.gotoAndPlay("no");
}
function MouseOverYes()
{
this.btn_yes.style.cursor = 'pointer';
}
function MouseOutYes()
{
this.btn_yes.style.cursor = 'default';
}
function MouseOverNo()
{
this.btn_no.style.cursor = 'pointer';
}
function MouseOutNo()
{
this.btn_no.style.cursor = 'default';
}
Maybe try this one.
stage.enableMouseOver();
var root = this;
root.theButtonName.cursor = "pointer";
stage.canvas.style.cursor = "default";
will set the mouse cursor to it's normal state.
stage.canvas.style.cursor = "none";
This will make the mouse cursor disappear, there's a serious lack of documentation on everything right now. Trying to find a good resource for coding in flash canvas as well. if you find one please let me know.
#user3570797
If you are using FlashCC, this can be done easily by creating button symbols. Try something like this: http://grab.by/Ktw2
Then reference these buttons by name via the 'ExportRoot' object and attach event handlers to those buttons.
Example:
function init() {
canvas = document.getElementById("canvas");
exportRoot = new lib.Button();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
stage.enableMouseOver();
var yesBtn = exportRoot.yesBtn;
var noBtn = exportRoot.noBtn;
yesBtn.addEventListener("click", handleClick);
noBtn.addEventListener("click", handleClick);
createjs.Ticker.setFPS(lib.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
}
function handleClick(event) {
//doSomething...
}
So i'm making this application with leafet.js.
This application requires that i have to manually draw grids onto the screen,
that i've taken care in a draw_grid() function that draws a bunch of polygons to the screen.
i have this function that i'm calling to trigger the change of the leaflet map.
zoom - the zoom integer and size is a dict like {x:1,y:1} that controls the size of the tiles drawn onto the map. (they need to change as the units the tiles are being drawn under are lat,long points on the map.
function changeZoom(zoom,size){
map.setZoom(zoom);
setSize(size);
setTimeout(drawGrid,500)s;
}
the reason i have to use the setTimeout is because the leaflet ignores any drawing commands onto the map (which i'm doing as a layer) until the map has finished animating.
how to do this asynchronously instead?
You can use the map.zoomend event, described in the API here
map.on('zoomend', function() {
drawGrid();
});
Once the map finishes the zooming animation, it will then call the drawGrid function.
In newer version of Leaflet, "zoomed" is no longer an event. There are now "zoomstart" and "zoomend" events:
map.on("zoomstart", function (e) { console.log("ZOOMSTART", e); });
map.on("zoomend", function (e) { console.log("ZOOMEND", e); });
This is best way to managed leflet Zoom control clicked
/*Zoom Control Click Managed*/
var bZoomControlClick = false;
mymap.on('zoomend',function(e){
var currZoom = mymap.getZoom();
if(bZoomControlClick){
console.log("Clicked "+currZoom);
}
bZoomControlClick = false;
});
var element = document.querySelector('a.leaflet-control-zoom-in');
L.DomEvent.addListener(element, 'click', function (e) {
bZoomControlClick = true;
$(mymap).trigger("zoomend");
});
var element1 = document.querySelector('a.leaflet-control-zoom-out');
L.DomEvent.addListener(element1, 'click', function (e) {
bZoomControlClick = true;
$(mymap).trigger("zoomend");
});