html5 canvas hand cursor problems - javascript

I'm playing around with html5 and some javascript to make a minor sketchpad. Whenever I click down on the canvas in chrome, the cursor becomes a text cursor. I have tried putting cursor: hand in the css, but that doesn't seem to work. This has got to be an easy thing, but I look it up and can't find it anywhere

Use the disable text selection on the canvas. This works like a charm.
var canvas = document.getElementById('canvas');
canvas.onselectstart = function () { return false; } // ie
canvas.onmousedown = function () { return false; } // mozilla
Cheers,
Kris

While the other guys were absolutely bang on referring you to the quirksmode reference, that won't fix the problem you are having, and essentially you need to implement a variation of Kris's answer.
In my own implementation, I found that preventing default behaviour in the mousedown event was all that was required to stop that pesky text selection cursor:
function handleMouseDown(evt) {
evt.preventDefault();
evt.stopPropagation();
// you can change the cursor if you want
// just remember to handle the mouse up and put it back :)
evt.target.style.cursor = 'move';
// rest of code goes here
}
document.addEventListener('mousedown', handleMouseDown, false);
Hope that helps.
Cheers,
Damon.

Use pointer for your cursor property instead, like this:
canvas { cursor: pointer; }
hand is IE/Opera specific, you can see a full list of which cursors work in which browsers here.

Related

Fabric (Canvas) on Mobile Devices while page scroll

I observed that we are not able to scroll web page by swiping fingers on mobile devices where we have used Canvas linked to Fabric covering full screen.
problem i due to canvase not allowing page scroll how can i fix it.
url : http://dev-shree.com/sendfile/
please check in mobile view.
Is there any way to fix it .
my code for canvase defination :
(function(global) {
"use strict";
var canvas = new fabric.Canvas('canvas',{selection : false,controlsAboveOverlay:true,centeredScaling:true});
var overlayimageurl = $('#myimg').val();
canvas.setOverlayImage(overlayimageurl, canvas.renderAll.bind(canvas));
canvas.stateful = false;
fabricjs canvas class has allowTouchScrolling option Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
var canvas = new fabric.Canvas('canvas',
{
selection : false,
controlsAboveOverlay:true,
centeredScaling:true,
allowTouchScrolling: true
}
);
What Tim Hill has said is correct. allowTouchScrolling: true, seems to do nothing. If you add pointer-events: none; to upper-canvas and lower-canvas, you can affect the ability to scroll the page while touching the canvas.
pointer-events: none;
will disable other events on canvas
I had the same issue and managed to solve it by implementing the above AND
ensuring all FabricJS objects (upper-canvas, canvas element, lower-canvas) had the CSS;
pointer-events: none;
Hope this helps
If any issue with page scrolling you can use this one in your CSS file :
canvas {
z-index: -1111;
}
If you are using version 2 or higher then you should update the fabric js.
Source: https://github.com/fabricjs/fabric.js/pull/5904/commits/50962efe6bbd77174cda2e9bd169c19d301edb7a
Then use allowTouchScrolling option.
var canvas = new fabric.Canvas('canvas',
{
allowTouchScrolling: true
}
);
All credit to #wnbittle and #alexey-altteam for their answer on the github issue#5903. Worked for me with fabricjs 4.2.0.
Add this function at the bottom of the fabric.js file:
(function(){
var defaultOnTouchStartHandler = fabric.Canvas.prototype._onTouchStart;
fabric.util.object.extend(fabric.Canvas.prototype, {
_onTouchStart: function(e) {
var target = this.findTarget(e);
// if allowTouchScrolling is enabled, no object was at the
// the touch position and we're not in drawing mode, then
// let the event skip the fabricjs canvas and do default
// behavior
if (this.allowTouchScrolling && !target && !this.isDrawingMode) {
// returning here should allow the event to propagate and be handled
// normally by the browser
return;
}
// otherwise call the default behavior
defaultOnTouchStartHandler.call(this, e);
}
});
})();

Global override of mouse cursor with JavaScript

In my web application I try to implement some drag and drop functionality. I have a global JavaScript component which does the the basic stuff. This object is also responsible for changing the mouse cursor, depending of the current drag operation (move, copy, link). On my web page there are various HTML elements which define an own cursor style, either inline or via a CSS file.
So, is there a way for my central drag and drop component to change the mouse cursor globally, independent from the style of the element the mouse cursor is over?
I tried:
document.body.style.cursor = "move"
and
document.body.style.cursor = "move !important"
But it doesn't work. Every time I drag over an element which defines a cursor style, the cursor changes to that style.
Sure, I could change the style of the element I'm currently dragging over, but then I have to reset it when I leave the element. This seems a little bit to complicated. I'm looking for a global solution.
Important Update (2021):
The MDN page for element.setCapture() clearly indicates that this feature is deprecated and non-standard, and should not be used in production.
The browser support table at the bottom of that page indicates that it's only supported in Firefox and IE.
Original answer below
Please: don't massacre your CSS!
To implement a drag and drop functionality, you have to use a very important API: element.setCapture(), which does the following :
All mouse events are redirected to the target element of the capture, regardless of where they occured (even outside the browser window)
The cursor will be the cursor of the target element of the capture, regardless where the mouse pointer is.
You have to call element.releaseCapture() or document.releaseCapture() to switch back to normal mode at the end of the operation.
Beware of a naïve implementation of drag and drop: you can have a lot of painful issues, like for example (among others): what happens if the mouse is released outside the browser's window, or over an element which has a handler that stops propagation. Using setCapture() solves all this issues, and the cursor style as well.
You can read this excellent tutorial that explains this in detail if you want to implement the drag and drop yourself.
Maybe you could also use jQuery UI draggable if possible in your context.
I tried using setPointerCapture which worked great. The downside is, that (of cause) all pointer events will not work as before. So I lost hover styles etc.
My solution now is pretty straight forward and for my usecase better suited then the above CSS solutions.
To set the cursor, I add a new stylesheet to head:
const cursorStyle = document.createElement('style');
cursorStyle.innerHTML = '*{cursor: grabbing!important;}';
cursorStyle.id = 'cursor-style';
document.head.appendChild(cursorStyle);
To reset it, I simply remove the stylesheet:
document.getElementById('cursor-style').remove();
document.body.style.cursor = "move"
should work just fine.
However, I recommend to do the global styling via CSS.
define the following:
body{
cursor:move;
}
The problem is, that the defined cursors on the other elements override the body style.
You could do someting like this:
your-element.style.cursor = "inherit"; // (or "default")
to reset it to the inherited style from the body or with CSS:
body *{
cursor:inherit;
}
Note however, that * is normally considered a bad selector-choice.
Unfortunately element.setCapture() does not work for IE
I use a brute force approach - open a transparent div on top of entire page for the duration of drag-drop.
.tbFiller {
position:absolute;
z-index:5000;
left:0;
top:0;
width:100%;
height:100%;
background-color:transparent;
cursor:move;
}
...
function dragStart(event) {
// other code...
document.tbFiller=document.createElement("div");
document.tbFiller.className="tbFiller"
}
function dragStop(event) {
// other code...
document.tbFiller.parentNode.removeChild(document.tbFiller);
}
Thanks to some of the other answers here for clues, this works well:
/* Disables all cursor overrides when body has this class. */
body.inheritCursors * {
cursor: inherit !important;
}
Note: I didn't need to use <html> and document.documentElement; instead, <body> and document.body work just fine:
document.body.classList.add('inheritCursors');
This causes all descendant elements of <body> (since it now has this inheritCursors class) to inherit their cursor from <body> itself, which is whatever you set it to:
document.body.style.cursor = 'progress';
Then to yield back control to the descendant elements, simply remove the class:
document.body.classList.remove('inheritCursors');
and to unset the cursor on the <body> to the default do:
document.body.style.cursor = 'unset';
This is what I do and it works in Firefox, Chrome, Edge and IE as of 2017.
Add this CSS rule to your page:
html.reset-all-cursors *
{
cursor: inherit !important;
}
When the <html> element has the "reset-all-cursors" class, this overrides all cursors that are set for elements individually in their style attribute – without actually manipulating the elements themselves. No need to clean up all over the place.
Then when you want to override your cursor on the entire page with that of any element, e. g. the element being dragged, do this in JavaScript:
element.setCapture && element.setCapture();
$("html").addClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", $(element).css("cursor"), "important");
It uses the setCapture function where it is available. This is currently just Firefox although they say it's a Microsoft API. Then the special class is added to the entire document, which disables all custom cursors. Finally set the cursor you want on the document so it should appear everywhere.
In combination with capturing events, this may even extend the dragging cursor to outside of the page and the browser window. setCapture does this reliably in Firefox. But elsewhere it doesn't work every time, depending on the browser, its UI layout, and the path along which the mouse cursor leaves the window. ;-)
When you're finished, clean up:
element.releaseCapture && element.releaseCapture();
$("html").removeClass("reset-all-cursors");
document.documentElement.style.setProperty("cursor", "");
This includes jQuery for addClass and removeClass. In simple scenarios you could just plain compare and set the class attribute of document.documentElement. This will break other libraries like Modernizr though. You can get rid of the css function if you know the element's desired cursor already, or try something like element.style.cursor.
A performance-acceptable, but not ideal either, solution that I ended up using, is actually to change the cursor prop of element directly under the pointer, and then return it back to original when pointer moved to another element. It works comparatively fast, as just a few elements change their style while moving pointer around, but visually you might sometimes see a short glimpse of "original" cursor. I consider it a much more acceptable tradeoff.
So, the solution, in TypeScript:
let prevElement: HTMLElement | undefined;
let prevElementOriginalCursor: string | undefined;
export const setTemporaryCursor = (element: HTMLElement, cursor: string | undefined) => {
// First, process the incoming element ASAP
let elementOriginalCursor: string | undefined;
requestAnimationFrame(() => {
if (element && prevElement !== element) {
elementOriginalCursor = element.style.cursor;
element.style.cursor = cursor ?? '';
}
});
// Then process the previous element, not so critical
requestAnimationFrame(() => {
if (prevElement && prevElement !== element) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = elementOriginalCursor;
}
prevElement = element;
});
};
export const resetTemporaryCursor = () => {
if (prevElement) {
prevElement.style.cursor = prevElementOriginalCursor ?? '';
prevElementOriginalCursor = undefined;
prevElement = undefined;
}
};
just call setTemporaryCursor while user moves mouse, and call resetTemporaryCursor() when drag process is wrapped up (on MouseUp for instance).
This does the job for me. The use of requestAnimationFrame is optional, and probably could be improved with experimentation.

How to detect CSS3 resize events

The CSS3 resize property can be assigned to arbitrary elements. I'm looking for a way to detect such a resize on, say, divs (I don't mind it only working in Firefox at the moment):
div {
resize: horizontal;
overflow: hidden;
}
Unfortunately, the onresize event seems not to be fired on the div. How can I detect in JavaScript when such a user-instantiated resize has happened?
Edit: FWIW I had opened a bug report over at Mozilla. If you want to track it: https://bugzilla.mozilla.org/show_bug.cgi?id=701648
Resizing is like a style change. As such it can be observed with a MutationObserver. The more specific ResizeObserver is probably even better:
let observer = new ResizeObserver(function(mutations) {
console.log('mutations:', mutations);
});
let child = document.querySelector('textarea');
observer.observe(child, { attributes: true });
<textarea></textarea>
Listen to DOMAttrModified events. Got the idea from this answer, this jsFiddle appears to work in Firefox 8 (if you open the console).
Since the resize event clearly doesn't work (currently, at least), you can try one of these alternative options:
Use a combination of mousedown, mousemove and/or mouseup to tell whether the div is being / has been resized. If you want really fine-grained control you can check in every mousemove event how much / if the div has been resized. If you don't need that, you can simply not use mousemove at all and just measure the div in mousedown and mouseup and figure out if it was resized in the latter.
Poll every 200ms or so (depending on your needs) and compare the current size with the last known size. See setTimeout().
You can use the ResizeSensor class of the css-element-queries polyfill from
https://github.com/marcj/css-element-queries
It allows you to call a javascript function on size changes for all types of elements, not only for window. It sets up a real sensor, not a javascript setTimeout poll.
Use it like this:
new ResizeSensor($('#myelement'), function() {
console.log("myelement's size has changed");
});
Supported browsers are: all incl. IE6+.
This seemed to work pretty well for me:
$("body").on('mousedown mousemove', ".resizeItem", function () {
console.log($(this).height());
})
"Use a combination of mousedown, mousemove and/or mouseup"
as per Felixs answer.
Especially useful when combining a custom search result panel with Dev Extremes Scroll View and you want full control over it.
$("body").on('mousedown mousemove', ".scrollContainer", function () {
var h = $(this).height() - 20;
$(".scrollArea").dxScrollView('instance').option('height', h);
});
According to this site it only works in internet explorer 9.
Try this fiddle: http://jsfiddle.net/maniator/3Zva3/

jQuery drag left/right

I've just created a custom carousel with images and have got previous/next arrows to move the images around.
Does jQuery have an event where I can click on the photo, drag it to the left or right and fire the same action that I currently have on the arrows?
My current code looks like this
$carousel.animate({
left: '+=' + amountToAnimate
}, 800, 'backEaseOut');
I also need to prevent Firefox from 'picking' the image up.
I'm already using jQuery UI if that helps.
You will need to add draggable() to your item and add some custom code the start event.
With more sample code it might be easier to give fuller advice, jsfiddle.net sample is best
EDIT:
You could use events api http://api.jquery.com/category/events/ , mousemove and mousedown, to figure which way to move the image, and call the animate event.
Re-using draggable , with some clever options and event functions may be better
I also need to prevent Firefox from 'picking' the image up.
Use the Mozilla CSS Extensions
-moz-user-focus:ignore;
-moz-user-select:none;
might to do the trick.
This may not be the most elegant solution but I believe it should do the job
// shortcut
var j = jQuery;
// add mouse down listener to the image
j('#image_id').mousedown(function(){
// add mouse move
j('#image_id').mouseMove(function(event){
// declare vars
var previous_x_position;
var previous_y_position;
if(x_position)
{
previous_x_position = x_position;
previous_y_position = y_position;
}
var x_position = event.pageX;
var y_position = event.pageY;
if(previous_x_position < x_position)
{
// we are moving right
}
else
{
// we are moving left
}
if(previous_y_position < y_position)
{
// we are moving down
}
else
{
// we are moving up
}
})
})

Custom cursor outside of browser window

I have a element on my website which is freely resizable. This is done by 4 handles on the edges. On hovering these handles and while resizing the element I want to show the respective resize arrows.
Currently I implemented this behavior by setting the css cursor style of the body/root to these arrows. The problem about it is the limit to the client area of the browser window. It would be visually more consistent and less confusing, if the arrow cursor would be visible everywhere while the mouse is hold down.
Google Maps is doing the same thing with their hand cursor while moving the map. So my question is how to achive this effect on my own.
My current (relevant) source:
function startObjectScaling(e){
e.stopPropagation();
e.preventDefault();
document.documentElement.style.cursor = this.style.cursor;
window.addEventListener("mouseup", stopObjectScaling, false);
}
function stopObjectScaling(e){
e.stopPropagation();
document.documentElement.style.cursor = '';
window.removeEventListener("mouseup", stopObjectScaling);
}
[...]
var tg = document.getElementById("transformGadget");
var handle = tg.firstChild.nextSibling;
for(var i=0;i<4;i++){
handle.addEventListener("mousedown", startObjectScaling, false);
handle = handle.nextSibling;
}
There is a special function implemented in the more modern browsers for this purpose. The name is setCapture(). It redirects all mouse input to the object the method was called on. Now a simple css cursor definition on that element is enough to archive the desired effect. After mouse release this effect stops (for security for sure). It can also be stopped manually by calling releaseCapture
example:
<style type="text/css">
#testObj {
/* this cursor will also stay outside the window.
could be set by the script at the mousedown event as well */
cursor: hand;
}
</style>
[...]
document.getElementById('testObj').onmousedown = function(e){
// these 2 might be useful in this context as well
//e.stopPropagation();
//e.preventDefault();
// here is the magic
e.target.setCapture();
}
if the arrow cursor would be visible everywhere while the mouse is hold down.
You're relying on a potential OS quirk to create your behavior. This is not something you can ASSUME will always hold true. However, once you start a mousedown, the cursor at that point will normally stay the same, no matter where you move the mouse to, UNTIL something else (another window that you may mouse over? the desktop? a system-interrupt?) changes the cursor.
In other words, don't rely on this behavior. Find something else that will work for you. If you must do this, re-examine your business requirements.

Categories