Canvas with mousemove blocking links in div underneath - javascript

I'm having problems with using the canvas/mousemove. I want to be able to draw on the entire page whenever the mouse moves with a mousemove draw/paint tool but also still click text links that appear in various other divs. The issue I have is that the canvas which is currently fixed, has a transparent background color and is set to 100% width and height blocks the div underneath with a lower z-index, meaning the links can't be clicked. Using pointer-events:none on the canvas isn't the solution as it disables the mousemove effect. If I make the canvas z-index lower than the div's with the links I want to click, the drawing will just appear outside of the div.
What do I need to add or change to make this work? I basically just want to have a functioning webpage with a mouseover effect that will draw over the page whenever it moves.
Below is the script I'm using. And here's an example http://jsfiddle.net/zAF4d/1/
$(function() {
var letsdraw = false;
var theCanvas = document.getElementById('paint');
var ctx = theCanvas.getContext('2d');
theCanvas.width = window.innerWidth;
theCanvas.height = window.innerHeight;
var canvasOffset = $('#paint').offset();
$('#paint').mousemove(function(e) {
if (letsdraw === true) {
ctx.lineTo(e.pageX - canvasOffset.left, e.pageY - canvasOffset.top);
ctx.stroke();
}
});
$('#paint').mousemove(function(e) {
$('.v').css('left', e.clientX + 'px');
$('.h').css('top', e.clientY + 'px');
letsdraw = true;
ctx.strokeStyle = 'blue';
ctx.lineWidth = 0.5;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(e.pageX - canvasOffset.left, e.pageY - canvasOffset.top);
});
$(window).mouseout(function(e) {
// bind to the window mouse up, that way if you mouse up and you're not over
// the canvas you'll still get the release of the drawing.
letsdraw = true;
});
});

You say:
The issue I have is that the canvas which is currently fixed, has a transparent background color and is set to 100% width and height blocks the div underneath with a lower z-index, meaning the links can't be clicked. Using pointer-events:none on the canvas isn't the solution as it disables the mousemove effect. If I make the canvas z-index lower than the div's with the links I want to click, the drawing will just appear outside of the div.
I think you need to either do everything on canvas or use DOM and some sort of CSS tricks/animations.

$(function() {
var letsdraw = false;
var theCanvas = document.getElementById('paint');
var ctx = theCanvas.getContext('2d');
.
.
.
//if your div's are not same every time, determine similar prop so u can pull it here on to a canvas...by'ID' || 'Class' ...etc.
var div = document.getElementByID('div01');
dix.x;
div.y;
div.h;
.
.
.
**etc. or if u cannot for any reason pull actual div, just pass on it parameters here....
then u can use them here...*
like making collision model for drawing line over the top of it...
if (mouse.x > div.x && mouse.x < div.x + div.width && mouse.y > div.y && mouse.y < div.height) {
letsdraw = false;
}
....**within draw function
**'onclick' event listener try **
$(div)onclick function();
if whole div was pulled here then it will open links...but even if u pulled just div's parameters u just pass click coordinates on to div if link coordinates match
$(div)onclick function(){
load.page(url: <link>your link</>
}
sorry if mistaken something, didn't run it all together
But give it a go hope it helps;

Related

JS-based hidden image breaks body width and height

I use a Zoom JavaScript for zoom my images when I click on it.
This JavaScript creates a hidden copy of my image with bigger dimensions.
Problem is, when I load my page, the body takes a height and width according to the hidden image.
You can see at the right of the screen the menu doesn't fit with the width of the screen (the hidden image is not displayed).
Is there a solution when I load the page, the size of the body does not take into account the hidden image?
// To achieve this effect we're creating a copy of the image and scaling it up. Next we'll pull the x and y coordinates of the mouse on the original image. Then we translate the big image so that the points we are looking at match up. Finally we create a mask to display the piece of the image that we're interested in.
let settings = {
'magnification': 3,
'maskSize': 200
}
// Once our images have loaded let's create the zoom
window.addEventListener("load", () => {
// find all the images
let images = document.querySelectorAll('.image-zoom-available');
// querySelectorAll produces an array of images that we pull out one by one and create a Zoombini for
Array.prototype.forEach.call(images, (image) => {
new Zoombini(image);
});
});
// A Zoombini (or whatever you want to call it), is a class that takes an image input and adds the zoomable functionality to it. Let's take a look inside at what it does.
class Zoombini {
// When we create a new Zoombini we run this function; it's called the constructor and you can see it taking our image in from above
constructor(targetImage) {
// We don't want the Zoombini to forget about it's image, so let's save that info
this.image = targetImage;
// The Zoombini isActive after it has opened up
this.isActive = false;
// But as it hasn't been used yet it's maskSize will be 0
this.maskSize = 0;
// And we have to start it's coordinates somewhere, they may as well be (0,0)
this.mousex = this.mousey = 0;
// Now we're set up let's build the necessary compoonents
// First let's clone our original image, I'm going to call it imageZoom and save it our Zoombini
this.imageZoom = this.image.cloneNode();
// And pop it next to the image target
this.image.parentNode.insertBefore(this.imageZoom, this.image);
// Make the zoom image that we'll crop float above it's original sibling
this.imageZoom.style.zIndex = 1;
// We don't want to be able to touch it though, we want to reach whats underneat
this.imageZoom.style.pointerEvents = "none";
// And so we can translate it let's make it absolute
this.imageZoom.style.position = "absolute";
// Now let's scale up our enlarged image and add an event listener so that it resizes whenever the size of the window changes
this.resizeImageZoom();
window.addEventListener("resize", this.resizeImageZoom.bind(this), false);
// Now that we're finishing the constructor we need to addeventlisteners so we can interact with it
// This function is just below, but still exists within our Zoombini
this.UI();
// Finally we'll apply an initial mask at default settings to hide this image
this.drawMask();
}
// resizeImageZoom resizes the enlarged image
resizeImageZoom() {
// So let's scale up this version
this.imageZoom.style.width = this.image.getBoundingClientRect().width * settings.magnification + 'px';
this.imageZoom.style.height = "unset"
}
// This could be inside the constructor but it's nicer on it's own I think
UI() {
this.image.addEventListener('mousemove', (event) => {
// When we move our mouse the x and y coordinates from the event
// We subtract the left and top coordinates so that we get the (x,y) coordinates of the actualy image, where (0,0) would be the top left
this.mousex = event.clientX - this.image.getBoundingClientRect().left;
this.mousey = event.clientY - this.image.getBoundingClientRect().top;
// if we're not active then don't display anything
if (!this.isActive) return;
// The drawMask() function below displays our the portion of the image that we're interested in
this.drawMask();
});
// When they mousedown we open up our mask
this.image.addEventListener('mousedown', () => {
// But it can be opening or closing, so let's pass in that information
this.isExpanding = true;
// To do that we start the maskSizer function, which calls itself until it reaches full size
this.maskSizer();
// And hide our cursor (we know where it is)
this.image.classList.add('is-active');
});
// if the mouse is released, close the mask
this.image.addEventListener('mouseup', () => {
// if it's not expanding, it's closing
this.isExpanding = false;
// if the mask has already expanded we'll need to start another maskSizer to shrink it. We don't run the maskSizer unless the mask is changing
if (this.isActive) this.maskSizer();
});
// same as above, caused by us moving out of the zoom area
this.image.addEventListener('mouseout', () => {
this.isExpanding = false;
if (this.isActive) this.maskSizer();
});
}
// The drawmask function shows us the piece of the image that we are hovering over
drawMask() {
// Let's use getBoundingClientRect to get the location of our images
let image = this.image.getBoundingClientRect();
let imageZoom = this.imageZoom.getBoundingClientRect();
// We'll start by getting the (x,y) of our big image that matches the piece we're mousing over (which we stored from our event listener as this.mousex and this.mousey). This is a clunky bit of code to help the zooms work in a variety of situations.
let prop_x = this.mousex / image.width * imageZoom.width * (1 - 1 / settings.magnification) - image.x - window.scrollX;
let prop_y = this.mousey / image.height * imageZoom.height * (1 - 1 / settings.magnification) - image.y - window.scrollY;
// Shift the large image by that amount
this.imageZoom.style.left = -prop_x + "px";
this.imageZoom.style.top = -prop_y + "px";
// Now we need to create our mask
// First let's get the coordinates of the point we're hovering over
let x = this.mousex * settings.magnification;
let y = this.mousey * settings.magnification;
// And create and apply our clip
let clippy = "circle(" + this.maskSize + "px at " + x + "px " + y + "px)";
this.imageZoom.style.clipPath = clippy;
this.imageZoom.style.webkitClipPath = clippy;
}
// We'll use the maskSizer to either expand or shrink the size of our mask
maskSizer() {
// We're in maskSizer so we're changing the size of our mask. Let's make the mask radius larger if the Zoombini is expanding, or shrink it if it's closing. The numbers below might need to be adjusted. It closes faster than it opens
this.maskSize = this.isExpanding ? this.maskSize + 35 : this.maskSize - 40;
// It has the form of: condition ? value-if-true : value-if-false
// Think of the ? as "then" and : as "else"
// if we've reaached max size, don't make it any larger
if (this.maskSize >= settings.maskSize) {
this.maskSize = settings.maskSize;
// we'll no longer need to change the maskSize so we'll just set this.isActive to true and let our mousemove do the drawing
this.isActive = true;
} else if (this.maskSize < 0) {
// Our mask is closed
this.maskSize = 0;
this.isActive = false;
this.image.classList.remove('is-active');
} else {
// Or else we haven't reached a size that we want to keep yet. So let's loop it on the next available frame
// We bind(this) here because so that the function remains in context
requestAnimationFrame(this.maskSizer.bind(this));
}
// After we have the appropriate size, draw the mask
this.drawMask();
}
}
function zoom(e) {
var zoomer = e.currentTarget;
e.offsetX ? offsetX = e.offsetX : offsetX = e.touches[0].pageX
e.offsetY ? offsetY = e.offsetY : offsetX = e.touches[0].pageX
x = offsetX / zoomer.offsetWidth * 100
y = offsetY / zoomer.offsetHeight * 100
zoomer.style.backgroundPosition = x + '% ' + y + '%';
}
//My image generated after page load
.image-zoom-available {
height: unset;
border-radius: 30px;
z-index: 1;
pointer-events: none;
position: absolute;
width: 834.688px;
left: 526.646px;
top: 231.729px;
clip-path: circle(0px at 439.062px 987.812px);
}
<div class="col-12 col-xl-3 col-lg-5 justify-content-center ">
<div class="mb-3">
<img class="image-zoom-available" style="height:75%; border-radius: 30px" src='{{ asset(' /radio/ ') }}{{examen.idpatient.id}}_examen_{{examen.id}}_radio.png' id="image_radio" draggable="false" />
</div>
</div>
Try adding the following property in your hidden image css :
display: none
A non visible element still take space in the web page. Cf: What is the difference between visibility:hidden and display:none?
Remove or override the display: none property when you want to display the image.
I add
this.imageZoom.style.display = "none";
on the event : mouseup and
this.imageZoom.style.display = "block";
on mousedown event. And it's fix thanks !

DIV with CSS resize attribute detectable in Java Script

A DIV can be resize using CSS like this
resize: both;
overflow: auto;
So, when you mouse over the DIV , you will get the resize cursor on the lower right corner.
I tried to detect the cursor change to "resize" in Java Script with no luck so far. I tried to log the cursor style with this code on a "click event" when the cursor change to "resize" like this:
console.log(document.body.style.cursor);
console.log(e.target.style.cursor);
The log result id always a " " (empty) instead of a "resize" value.
Thanks in advance for your help.
Yes, I checked, string is empty for e.target.style.cursor. So I propose to solve the issue with a hack. We know, that an area where cursor changes is about 18×18 pixels, so we should detect cursor at this zone.
const textarea = document.querySelector('textarea');
const deltaX = 18;
const deltaY = 18;
const detectionOfCursorInLowerRightCorner = (e) => {
const rect = e.target.getBoundingClientRect();
const x = e.clientX; // Position X of cursor relative to the document.
const y = e.clientY; // Position Y of cursor relative to the document.
// Calculate lower and high edges of the area where cursor changes.
const lowEdgeX = rect.width - deltaX;
const highEdgeX = rect.width;
const lowEdgeY = rect.height - deltaY;
const highEdgeY = rect.height;
const elementCursorX = x - rect.x; // Position X of cursor relative to the element.
const elementCursorY = y - rect.y; // Position Y of cursor relative to the element.
console.log(' ');
console.log(elementCursorX, elementCursorY);
if (elementCursorX > lowEdgeX && elementCursorX <= highEdgeX && elementCursorY > lowEdgeY && elementCursorY <= highEdgeY) {
console.log('here'); // Cursor is in the area.
}
};
textarea.addEventListener('mousemove', detectionOfCursorInLowerRightCorner);
Do not forget do textarea.removeEventListener('mousemove', detectionOfCursorInLowerRightCorner) on element destroy. And define your own CSS class to detect when textarea has properties resize: both; and overflow: auto;.
Thanks for your answer. Indeed, I did something approximative (I skipped the Y axis logic) similar to your solution. On mousemove in the DIV, I consider that resize will occurs if my mouse X position is between the offetWidth-15px and offsetx. If not, I can drag my DIV.
I considered also the resize observer feature, but it was getting too complicated for multiple DIV elements.
The idea is to be add drag/resize events to my DIV elements for editing purpose over a canvas using absolute positioning.
Congratulation for your concise and well formatted answer and the time spent you spent on it.

Trying to make a drag to stretch element, why is it so jumpy?

I'm trying to create a dragbar so users can stretch the height or width of an element on my page (not interested in using HTML resize).
It seems like I'm pretty close, but I can't figure out why
1) the moveable bar is jumping all over the page
2) the adjustable div is flickering as the size changes (or sometimes disappearing completely).
You can see the demo at http://jsfiddle.net/dYUz7/
Here's the source
var elem = $('.layout');
var resizeBar = $('.resize-bar',elem),
adjustableWrapper = $('.layout-container-wrapper',elem),
posDir = 'Left',
pos = 'X';
if($(elem).hasClass('layout-updown')){
posDir = 'Top';
pos = 'Y';
}
var startPos = resizeBar[0]['offset'+posDir], i = resizeBar[0]['offset'+posDir];
resizeBar.on('mousedown', function(event) {
// Prevent default dragging of selected content
event.preventDefault();
console.log(event);
$(document).on('mousemove', mousemove);
$(document).on('mouseup', mouseup);
});
function mousemove(event) {
i = event['page'+pos] - startPos;
if(pos==='X') return changeSizeWidth(i,event.offsetX);
return changeSizeHeight(i,event.offsetY);
}
function changeSizeWidth(i,width){
resizeBar.css({left : i +'px'});
adjustableWrapper.css({'width': width +'px'});
console.log(adjustableWrapper.css('width'));
}
function changeSizeHeight(i,height){
resizeBar.css({top : i +'px'});
adjustableWrapper.css({'height': height +'px'});
console.log(adjustableWrapper.css('height'));
}
function mouseup() {
$(document).unbind('mousemove', mousemove);
$(document).unbind('mouseup', mouseup);
}
Please don't respond with suggestions for using libraries, I have jQuery in the sample, but I'm using angular in the project, and am trying to not add a bunch of other libraries at this point.
I ended up getting this working by changing the move from the move-bar element to the element which needs to grow or shrink.
The css is a bit messed up, but here's an updated jsfiddle of the result
http://jsfiddle.net/dYUz7/2/
function changeSizeWidth(left){
adjustableWrapper.css({'width': left +'px'});
}

without jquery i need to find out if the mouse is over an element, not determine when it becomes over (in case it doesn't move to trigger onmouseover)

without jquery
basically what I am looking for is the ability to see if the mouse is over a div when a countdown finishes
if the user is over the div then perform action for that div
onmouseover only triggers when the mouse crosses the threshold of the div, if the mouse hasn't moved it wouldn't trigger, so that wouldn't work
I need to determine if the mouse is currently over a div at a specific point in time, if it has moved or not from the starting point
all of my hunting has only found onmousover, and nothing to see if the mouse just happens to be there to begin with
I don't have the javascript skills to determine overall coords of div, then map mouse coords and see if it fits there... which is what I believe I need to do
After reading the second answer (the one with millions of a elements) on this SO question, I've came up with this method works without moving the mouse on page load, without involving millions of elements.
HTML
<div id=t></div>
CSS
#t {
/* for illustrative purposes */
width: 10em;
height: 5em;
background-color: #0af;
}
#t:hover {
border-top-style: hidden;
}
JavaScript
document.addEventListener('click', function () {
var c = window.getComputedStyle(document.getElementById('t')).getPropertyValue('border-top-style');
if (c === 'hidden') {
alert('Mouse in box');
} else {
alert('Mouse not in box');
}
}, false);
As stated earlier, bind to the finish event of your countdown instead of the click event on the document.
You may also use any CSS style that's changed on :hover, I chose border-top-style as it is conspicuous. If you're using a border, choose something else.
Here's a jsFiddle.
set a flag to true onmouseover and to false onmouseleave. when countdown finishes if flag is true then it is over element.
HTML
<div id="div-name">the section of the code i am working with has a countdown timer, when it reaches 0 i need to know if the mouse is over a specific box</div>
<button id="notification" onclick="javascript: letsCountIt(5);">click to start countdown</button>
JS
window.ev = false;
document.getElementById('div-name').onmouseover = function () {
window.ev = true;
console.log(window.ev);
}
document.getElementById('div-name').onmouseout = function () {
window.ev = false;
console.log(window.ev);
}
window.letsCountIt = function (cdtimer) {
cdtimer--;
document.getElementById('notification').innerHTML = cdtimer;
if (cdtimer == 0) {
if (window.ev === true) {
alert('over');
} else {
alert('not over');
}
} else {
setTimeout(function(){letsCountIt(cdtimer);}, 1000);
}
}
Look into document.elementFromPoint . When you pass an x,y to elementFromPoint, it will return whatever element (or <body>, if no other specific element) is at that point. You can easily check if this element is the element you want.
The problem then is finding out what point your mouse is at. How to get the mouse position without events (without moving the mouse)? seems to say - don't. At least use mouseMove to track the cursor. The linked question gives examples of how to do so. (Look to the lower scoring answers, as the higher ones only got points for being snarky.)
Just want to say that, I think jQuery's mouseenter and mouseleave events would make this a lot easier, but if you can't use them, maybe this will help you.
Depending on how your page is laid out, this may not be too difficult. You can get the position of your element using the following. Quoting from another answer
element.offsetLeft and element.offsetTop are the pure javascript
properties for finding an element's position with respect to its
offsetParent; being the nearest parent element with a position of
relative or absolute
So, if your element is positioned relatively to the body, so far so good (We don't need to adjust anything).
Now, if we attach an event to the document mousemove event, we can get the current coordinates of the mouse:
document.addEventListener('mousemove', function (e) {
var x = e.clientX;
var y = e.clientY;
}, false);
Now we just need to determine if the mouse falls within the element. To do that we need the height and width of the element. Quoting from another answer
You should use the .offsetWidth and .offsetHeight properties. Note
they belong to the element, not .style.
For example:
var element = document.getElementById('element');
var height = element.offsetHeight;
var width = element.offsetWidth;
Now we have all the information we need, and just need to determine if the mouse falls within the element. We might use something like this:
var onmove = function(e) {
var minX = element.offsetLeft;
var maxX = minX + element.offsetWidth;
var minY = element.offsetTop;
var maxY = minY + element.offsetHeight;
if(e.clientX >= minX && e.clientX <= maxX)
//good horizontally
if(e.clientY >= minY && e.clientY <= maxY)
//good vertically
}
This code works, but the mouse has to be moved once after page load.
var coords;
var getMouseCoordinates = function (e) {
'use strict';
return {
x: e.clientX,
y: e.clientY
};
};
document.addEventListener('mousemove', function (e) {
coords = getMouseCoordinates(e);
}, false);
document.addEventListener('click', function () {
var divCoords = document.getElementById('t').getBoundingClientRect();
if (coords.x >= divCoords.left && coords.x <= divCoords.right && coords.y >= divCoords.top && coords.y <= divCoords.bottom) {
alert('Mouse in box');
} else {
alert('Mouse not in box');
}
}, false);
You wouldn't bind to the click event of document, but rather the finish event of your countdown.
Here's an example. Try clicking in the output window.
You don't need any coordinates or mouse events, if you know a selector for that element:
if (document.querySelector('#elementSelector:hover')) {
alert('I like it when you touch me!');
}

Keep focus on element behind other (z-order) when hovering

I've got an image, and using JQuery I've got it so that the image will increase in size when you hover over it and a DIV that sits under the image will slide up and provide an overlay on the image that is half opacity and occupy the bottom half of the image area.
This works fine, however when you then hover over the overlay (but still on the same area as the image), the image scales down again because you're no longer focused on the image, so the hover out event gets fired.
How do you ignore the overlay when hovering as if to say "I don't care when you hover over the overlay, so long as you're still in the area of the image"?
You could use mousemove event instead of mouseenter and mouseleave and use image dimensions to determine if mouse is over the image.
Example:
$(window).mousemove(function(evt) {
var image = $("#image");
var imageX0 = image.offset().left
var imageY0 = image.offset().top
var imageX1 = imageX0 + image.width()
var imageY1 = imageY0 + image.height()
if( evt.pageX >= imageX0 && evt.pageX <= imageX1 && evt.pageY >= imageY0 && evt.pageY <= imageY1) {
$("#status").text('Hovered!')
} else {
$("#status").text('Not hovered')
}
})
Check out live here: http://dl.dropbox.com/u/486054/stackoverflow/hover-overlay.html

Categories

Resources