Konva events mis-positioned after screen resize - javascript

In my code I am resizing the Konva canvas and the konvajs-content elements to maintain a full screen appearance.
resizeCanvas()
window.onresize = function() { resizeCanvas(); }
function resizeCanvas() {
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
x = w.innerWidth || e.clientWidth || g.clientWidth,
y = w.innerHeight || e.clientHeight || g.clientHeight;
var ratio = x / y;
var should = 1920 / 1080;
var cv = document.getElementsByTagName("canvas")[0];
var cc = document.getElementsByClassName("konvajs-content")[0];
var cx, cy;
var cleft=0;
if(ratio > should) {
cx = (y * 1920/1080);
cy = y;
cleft = (x - cx) / 2;
} else {
cx = x;
cy = (x * 1080/1920);
cv.setAttribute("style", "width:" + x + "px;height:"+ (x*1080/1920) + "px;");
}
cc.setAttribute("style", "width:" + x + "px;height: " + y + "px;");
cv.setAttribute("style", "width:" + cx + "px;heigth: " + cy + "px; position: relative; left: " + cleft + "px");
}
This all works great until I try to capture any event input. As you can see in the JSFiddle, when trying to click on the 'Test' text you will not cause an event to fire.
But if you click ~40px to the left and 20px above the text you will fire the events.
https://jsfiddle.net/3qc3wtr3/2/
Is there a way to keep the resize behavior and to ensure that events are being fired based on the actual location of the elements?

You should use stage.width() and stage.height() to fit a page. You can add scale into stage if you need.

Related

Position div at exact cursors location

I am using this color wheel picker, and I'm trying to add a div as the dragger instead of having it embedded in the canvas. I got it working thanks to these answers.
The problem is, the dragger is a bit off from the cursor. The obvious solution would be to just subtract from the draggers left and top position. Like this:
dragger.style.left = (currentX + radiusPlusOffset - 13) + 'px';
dragger.style.top = (currentY + radiusPlusOffset - 13) + 'px';
Another problem comes up when I subtract 13. If you drag the dragger all the way to the right or bottom, it doesn't go all the way. If you drag it all the way to the left or top, it goes passed the canvas's border.
Basically what I'm trying to achieve, is to have the dragger at the cursor pointers exact location, and the draggable shouldn't go passed the canvas's border. How can I achieve that?
JSFiddle
var b = document.body;
var c = document.getElementsByTagName('canvas')[0];
var a = c.getContext('2d');
var wrapper = document.getElementById('wrapper');
var dragger = document.createElement('div');
dragger.id = 'dragger';
wrapper.appendChild(dragger);
wrapper.insertBefore(dragger, c);
document.body.clientWidth; // fix bug in webkit: http://qfox.nl/weblog/218
(function() {
// Declare constants and variables to help with minification
// Some of these are inlined (with comments to the side with the actual equation)
var doc = document;
doc.c = doc.createElement;
b.a = b.appendChild;
var width = c.width = c.height = 400,
label = b.a(doc.c("p")),
input = b.a(doc.c("input")),
imageData = a.createImageData(width, width),
pixels = imageData.data,
oneHundred = input.value = input.max = 100,
circleOffset = 0,
diameter = width - circleOffset * 2,
radius = diameter / 2,
radiusPlusOffset = radius + circleOffset,
radiusSquared = radius * radius,
two55 = 255,
currentY = oneHundred,
currentX = -currentY,
wheelPixel = circleOffset * 4 * width + circleOffset * 4;
// Math helpers
var math = Math,
PI = math.PI,
PI2 = PI * 2,
sqrt = math.sqrt,
atan2 = math.atan2;
// Setup DOM properties
b.style.textAlign = "center";
label.style.font = "2em courier";
input.type = "range";
// Load color wheel data into memory.
for (y = input.min = 0; y < width; y++) {
for (x = 0; x < width; x++) {
var rx = x - radius,
ry = y - radius,
d = rx * rx + ry * ry,
rgb = hsvToRgb(
(atan2(ry, rx) + PI) / PI2, // Hue
sqrt(d) / radius, // Saturation
1 // Value
);
// Print current color, but hide if outside the area of the circle
pixels[wheelPixel++] = rgb[0];
pixels[wheelPixel++] = rgb[1];
pixels[wheelPixel++] = rgb[2];
pixels[wheelPixel++] = d > radiusSquared ? 0 : two55;
}
}
a.putImageData(imageData, 0, 0);
// Bind Event Handlers
input.onchange = redraw;
dragger.onmousedown = c.onmousedown = doc.onmouseup = function(e) {
// Unbind mousemove if this is a mouseup event, or bind mousemove if this a mousedown event
doc.onmousemove = /p/.test(e.type) ? 0 : (redraw(e), redraw);
}
// Handle manual calls + mousemove event handler + input change event handler all in one place.
function redraw(e) {
// Only process an actual change if it is triggered by the mousemove or mousedown event.
// Otherwise e.pageX will be undefined, which will cause the result to be NaN, so it will fallback to the current value
currentX = e.pageX - c.offsetLeft - radiusPlusOffset || currentX;
currentY = e.pageY - c.offsetTop - radiusPlusOffset || currentY;
// Scope these locally so the compiler will minify the names. Will manually remove the 'var' keyword in the minified version.
var theta = atan2(currentY, currentX),
d = currentX * currentX + currentY * currentY;
// If the x/y is not in the circle, find angle between center and mouse point:
// Draw a line at that angle from center with the distance of radius
// Use that point on the circumference as the draggable location
if (d > radiusSquared) {
currentX = radius * math.cos(theta);
currentY = radius * math.sin(theta);
theta = atan2(currentY, currentX);
d = currentX * currentX + currentY * currentY;
}
label.textContent = b.style.background = hsvToRgb(
(theta + PI) / PI2, // Current hue (how many degrees along the circle)
sqrt(d) / radius, // Current saturation (how close to the middle)
input.value / oneHundred // Current value (input type="range" slider value)
)[3];
dragger.style.left = (~~currentX + radiusPlusOffset - 13) + 'px';
dragger.style.top = (~~currentY + radiusPlusOffset - 13) + 'px';
// Reset to color wheel and draw a spot on the current location.
// Draw the current spot.
// I have tried a rectangle, circle, and heart shape.
/*
// Rectangle:
a.fillStyle = '#000';
a.fillRect(currentX+radiusPlusOffset,currentY+radiusPlusOffset, 6, 6);
*/
// Circle:
/*a.beginPath();
a.strokeStyle = 'white';
a.arc(~~currentX+radiusPlusOffset,~~currentY+radiusPlusOffset, 4, 0, PI2);
a.stroke();*/
// Heart:
//a.font = "1em arial";
//a.fillText("♥", currentX + radiusPlusOffset - 4, currentY + radiusPlusOffset + 4);
}
// Created a shorter version of the HSV to RGB conversion function in TinyColor
// https://github.com/bgrins/TinyColor/blob/master/tinycolor.js
function hsvToRgb(h, s, v) {
h *= 6;
var i = ~~h,
f = h - i,
p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s),
mod = i % 6,
r = [v, q, p, p, t, v][mod] * two55,
g = [t, v, v, q, p, p][mod] * two55,
b = [p, p, t, v, v, q][mod] * two55;
return [r, g, b, "rgb(" + ~~r + "," + ~~g + "," + ~~b + ")"];
}
// Kick everything off
redraw(0);
/*
// Just an idea I had to kick everything off with some changing colors…
// Probably no way to squeeze this into 1k, but it could probably be a lot smaller than this:
currentX = currentY = 1;
var interval = setInterval(function() {
currentX--;
currentY*=1.05;
redraw(0)
}, 7);
setTimeout(function() {
clearInterval(interval)
}, 700)
*/
})();
#c {
border: 7px solid black;
border-radius: 50%;
}
#wrapper {
width: 400px;
height: 400px;
position: relative;
cursor: pointer;
}
#wrapper:active {
//cursor: none;
}
#dragger {
width: 8px;
height: 8px;
border-radius: 50%;
display: block;
position: absolute;
border: 2px solid black;
}
<div id='wrapper'>
<canvas id="c"></canvas>
</div>
You're just subtracting in the wrong place.
Instead of subtracting from the elements position, subtract directly from the mouse pointers position.
This code actually moves the element, offsetting it relative to the pointer, and making it appear to be outside the borders of the color picker
dragger.style.left = (~~currentX + radiusPlusOffset - 13) + 'px';
dragger.style.top = (~~currentY + radiusPlusOffset - 13) + 'px';
... which is not what you really want, you want the calculated numbers for the pointer to be exactly center of the dragger element, so you should extract from the pointers position instead, that way the limits of the dragger isn't affected, and it stays within the borders of the color picker
currentX = e.pageX - c.offsetLeft - radiusPlusOffset -13 || currentX;
currentY = e.pageY - c.offsetTop - radiusPlusOffset -13 || currentY;
FIDDLE

Mouse coordinate based perspective rotation with centered div [Javascript]

Second question I've asked, so if I need to add anything else, let me know please!
Been looking everywhere to try and understand concepts, but I feel like I am over thinking it. What I am trying to do is base "transform: rotateX("x"deg)" off of the x location of the users cursor proportionately from the size of the window. Here is a gif that does what I want, but they use matrix3D instead of just transform's rotate.
http://i.imgur.com/tUZSLXA.gif
Here is a js fiddle of what I have so far.
https://jsfiddle.net/z7bet2sw/
JS:
function rotate() {
var x = event.clientX;
var w = window.innerWidth;
var midpoint = w / 2;
// restrictions for rotation
if (x > -20 && x < 20) {
document.getElementsById("logo").style.transform = "perspective(550px)" + "rotateY(" + x + "deg)";
} else {
}
}
document.addEventListener("mousemove", rotate);
Thanks!
You need to pass the event parameter to rotate function from addEventListener, and also you need to calculate a relative position from the x coordinates to get a value between -20 and 20:
function rotate (event)
{
var x = event.clientX;
var w = window.innerWidth;
var midpoint = w / 2;
var pos = x - midpoint;
var val = (pos / midpoint) * 20;
var logo = document.getElementById ("logo");
logo.style.transform = "perspective(550px) rotateY(" + val + "deg)";
}
document.addEventListener("mousemove", function(event)
{
rotate(event)
}, false);
JSFiddle
Two issues:
you are using document.getElementsById which should actually be document.getElementById (no s)
you should subtract midpoint from x
Updated code:
function rotate() {
var x = event.clientX;
var w = window.innerWidth;
var midpoint = w / 2;
x -= midpoint;
if (x > -20 && x < 20) {
document.getElementById("logo").style.transform = "perspective(550px)" + "rotateY(" + x + "deg)";
} else {
}
}
fiddle: https://jsfiddle.net/jacquesc/z7bet2sw/1/

Deg. in rotation

I am building time picker where you can set time on "real" clocks.
Im finding the position of mouse and degrees + rotating div using this script
var g;
var h;
if (e.target.id == "rotationSliderContainer") {
g = e.offsetX;
h = e.offsetY;
} else {
g = e.target.offsetLeft + e.offsetX;
h = e.target.offsetTop + e.offsetY;
}
var atan = Math.atan2(g - radius, h - radius);
deg = -atan / (Math.PI / 180) + 180;
var presne = Math.abs(deg - (Math.round(deg / 30) * 30));
if (presne <= 2) deg = Math.round(deg / 30) * 30;
if(deg == 360)
deg = 0;
two.style.transform = "rotate(" + deg + "deg)";
it works fine but it has little bug , whenever i want to move div , between 12 and 1,2,3 its flipping between 12 and one of those values.
Here is a live demo http://jsfiddle.net/Trolstover/afo7ky03/1/
Is there any shortcut how to prevent this unwanted behavior or did i simply made a mistake somewhere?
This if/else condition is switching between events in the rotationSliderContainer and other DOM elements (such as the rotationSlider and the body). That else condition is your culprit.
if (e.target.id == "rotationSliderContainer") {
g = e.offsetX;
h = e.offsetY;
} else {
g = e.target.offsetLeft + e.offsetX;
h = e.target.offsetTop + e.offsetY;
}
remove those two lines and it should be fine.
if (e.target.id == "rotationSliderContainer") {
g = e.offsetX;
h = e.offsetY;
}
Cool clock ^_^

Pinch to zoom with Hammer.js

I want a pinch to Zoom function for an image. I want it to zoom in the area where the fingers are.
My index is only
<div id="wrapper" style="-webkit-transform: translate3d(0,0,0);overflow: hidden;">
</div>
And my script for zooming and scrolling is similar with this example. I have made a few changes to fit with my project
My problem is in
case 'transform':
rotation = last_rotation + ev.gesture.rotation;
scale = Math.max(1, Math.min(last_scale * ev.gesture.scale, 10));
break;
How can I change it so that it doesn't zoom into the center of the picture but at place where the first finger have touch the display?
Sorry for my bad english :)
This is an example with hammer.js and tap. As you tap it will zoom in at the point where you tapped. The event data is common for all gestures so switching from tap to pinch should work. It is a good example to work on. You may need to increase the scale step as you pinch. It has been tested on chrome(v30) and firefox (v24).
It is based on the solution mentioned at the thread,
Zoom in on a point (using scale and translate)
as you will see an alternative could also be to use canvas.
HTML
<div style="-webkit-transform: translate3d(0,0,0);overflow: hidden;" class="zoomable">
<img src="http://i.telegraph.co.uk/multimedia/archive/01842/landscape-rainbow_1842437i.jpg" />
</div>
JS
(function ($) {
$(document).ready(function () {
var scale = 1; // scale of the image
var xLast = 0; // last x location on the screen
var yLast = 0; // last y location on the screen
var xImage = 0; // last x location on the image
var yImage = 0; // last y location on the image
Hammer($('.zoomable img').get(0)).on("tap", function (event) {
var posX = event.gesture.center.pageX;
var posY = event.gesture.center.pageY;
// find current location on screen
var xScreen = posX; //- $(this).offset().left;
var yScreen = posY; //- $(this).offset().top;
// find current location on the image at the current scale
xImage = xImage + ((xScreen - xLast) / scale);
yImage = yImage + ((yScreen - yLast) / scale);
scale++;
// determine the location on the screen at the new scale
var xNew = (xScreen - xImage) / scale;
var yNew = (yScreen - yImage) / scale;
// save the current screen location
xLast = xScreen;
yLast = yScreen;
// redraw
$(this).css('-webkit-transform', 'scale(' + scale + ')' + 'translate(' + xNew + 'px, ' + yNew + 'px' + ')')
.css('-webkit-transform-origin', xImage + 'px ' + yImage + 'px').css('-moz-transform', 'scale(' + scale + ') translate(' + xNew + 'px, ' + yNew + 'px)').css('-moz-transform-origin', xImage + 'px ' + yImage + 'px')
.css('-o-transform', 'scale(' + scale + ') translate(' + xNew + 'px, ' + yNew + 'px)').css('-o-transform-origin', xImage + 'px ' + yImage + 'px').css('transform', 'scale(' + scale + ') translate(' + xNew + 'px, ' + yNew + 'px)');
});
});
})(jQuery);
http://jsfiddle.net/SySZL/
Check out the Pinch Zoom and Pan with HammerJS demo. This example has been tested on Android, iOS and Windows Phone.
You can find the source code at Pinch Zoom and Pan with HammerJS.
For your convenience, here is the source code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<title>Pinch Zoom</title>
</head>
<body>
<div>
<div style="height:150px;background-color:#eeeeee">
Ignore this area. Space is needed to test on the iPhone simulator as pinch simulation on the
iPhone simulator requires the target to be near the middle of the screen and we only respect
touch events in the image area. This space is not needed in production.
</div>
<style>
.pinch-zoom-container {
overflow: hidden;
height: 300px;
}
.pinch-zoom-image {
width: 100%;
}
</style>
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
<script>
var MIN_SCALE = 1; // 1=scaling when first loaded
var MAX_SCALE = 64;
// HammerJS fires "pinch" and "pan" events that are cumulative in nature and not
// deltas. Therefore, we need to store the "last" values of scale, x and y so that we can
// adjust the UI accordingly. It isn't until the "pinchend" and "panend" events are received
// that we can set the "last" values.
// Our "raw" coordinates are not scaled. This allows us to only have to modify our stored
// coordinates when the UI is updated. It also simplifies our calculations as these
// coordinates are without respect to the current scale.
var imgWidth = null;
var imgHeight = null;
var viewportWidth = null;
var viewportHeight = null;
var scale = null;
var lastScale = null;
var container = null;
var img = null;
var x = 0;
var lastX = 0;
var y = 0;
var lastY = 0;
var pinchCenter = null;
// We need to disable the following event handlers so that the browser doesn't try to
// automatically handle our image drag gestures.
var disableImgEventHandlers = function () {
var events = ['onclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
'onmouseup', 'ondblclick', 'onfocus', 'onblur'];
events.forEach(function (event) {
img[event] = function () {
return false;
};
});
};
// Traverse the DOM to calculate the absolute position of an element
var absolutePosition = function (el) {
var x = 0,
y = 0;
while (el !== null) {
x += el.offsetLeft;
y += el.offsetTop;
el = el.offsetParent;
}
return { x: x, y: y };
};
var restrictScale = function (scale) {
if (scale < MIN_SCALE) {
scale = MIN_SCALE;
} else if (scale > MAX_SCALE) {
scale = MAX_SCALE;
}
return scale;
};
var restrictRawPos = function (pos, viewportDim, imgDim) {
if (pos < viewportDim/scale - imgDim) { // too far left/up?
pos = viewportDim/scale - imgDim;
} else if (pos > 0) { // too far right/down?
pos = 0;
}
return pos;
};
var updateLastPos = function (deltaX, deltaY) {
lastX = x;
lastY = y;
};
var translate = function (deltaX, deltaY) {
// We restrict to the min of the viewport width/height or current width/height as the
// current width/height may be smaller than the viewport width/height
var newX = restrictRawPos(lastX + deltaX/scale,
Math.min(viewportWidth, curWidth), imgWidth);
x = newX;
img.style.marginLeft = Math.ceil(newX*scale) + 'px';
var newY = restrictRawPos(lastY + deltaY/scale,
Math.min(viewportHeight, curHeight), imgHeight);
y = newY;
img.style.marginTop = Math.ceil(newY*scale) + 'px';
};
var zoom = function (scaleBy) {
scale = restrictScale(lastScale*scaleBy);
curWidth = imgWidth*scale;
curHeight = imgHeight*scale;
img.style.width = Math.ceil(curWidth) + 'px';
img.style.height = Math.ceil(curHeight) + 'px';
// Adjust margins to make sure that we aren't out of bounds
translate(0, 0);
};
var rawCenter = function (e) {
var pos = absolutePosition(container);
// We need to account for the scroll position
var scrollLeft = window.pageXOffset ? window.pageXOffset : document.body.scrollLeft;
var scrollTop = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;
var zoomX = -x + (e.center.x - pos.x + scrollLeft)/scale;
var zoomY = -y + (e.center.y - pos.y + scrollTop)/scale;
return { x: zoomX, y: zoomY };
};
var updateLastScale = function () {
lastScale = scale;
};
var zoomAround = function (scaleBy, rawZoomX, rawZoomY, doNotUpdateLast) {
// Zoom
zoom(scaleBy);
// New raw center of viewport
var rawCenterX = -x + Math.min(viewportWidth, curWidth)/2/scale;
var rawCenterY = -y + Math.min(viewportHeight, curHeight)/2/scale;
// Delta
var deltaX = (rawCenterX - rawZoomX)*scale;
var deltaY = (rawCenterY - rawZoomY)*scale;
// Translate back to zoom center
translate(deltaX, deltaY);
if (!doNotUpdateLast) {
updateLastScale();
updateLastPos();
}
};
var zoomCenter = function (scaleBy) {
// Center of viewport
var zoomX = -x + Math.min(viewportWidth, curWidth)/2/scale;
var zoomY = -y + Math.min(viewportHeight, curHeight)/2/scale;
zoomAround(scaleBy, zoomX, zoomY);
};
var zoomIn = function () {
zoomCenter(2);
};
var zoomOut = function () {
zoomCenter(1/2);
};
var onLoad = function () {
img = document.getElementById('pinch-zoom-image-id');
container = img.parentElement;
disableImgEventHandlers();
imgWidth = img.width;
imgHeight = img.height;
viewportWidth = img.offsetWidth;
scale = viewportWidth/imgWidth;
lastScale = scale;
viewportHeight = img.parentElement.offsetHeight;
curWidth = imgWidth*scale;
curHeight = imgHeight*scale;
var hammer = new Hammer(container, {
domEvents: true
});
hammer.get('pinch').set({
enable: true
});
hammer.on('pan', function (e) {
translate(e.deltaX, e.deltaY);
});
hammer.on('panend', function (e) {
updateLastPos();
});
hammer.on('pinch', function (e) {
// We only calculate the pinch center on the first pinch event as we want the center to
// stay consistent during the entire pinch
if (pinchCenter === null) {
pinchCenter = rawCenter(e);
var offsetX = pinchCenter.x*scale - (-x*scale + Math.min(viewportWidth, curWidth)/2);
var offsetY = pinchCenter.y*scale - (-y*scale + Math.min(viewportHeight, curHeight)/2);
pinchCenterOffset = { x: offsetX, y: offsetY };
}
// When the user pinch zooms, she/he expects the pinch center to remain in the same
// relative location of the screen. To achieve this, the raw zoom center is calculated by
// first storing the pinch center and the scaled offset to the current center of the
// image. The new scale is then used to calculate the zoom center. This has the effect of
// actually translating the zoom center on each pinch zoom event.
var newScale = restrictScale(scale*e.scale);
var zoomX = pinchCenter.x*newScale - pinchCenterOffset.x;
var zoomY = pinchCenter.y*newScale - pinchCenterOffset.y;
var zoomCenter = { x: zoomX/newScale, y: zoomY/newScale };
zoomAround(e.scale, zoomCenter.x, zoomCenter.y, true);
});
hammer.on('pinchend', function (e) {
updateLastScale();
updateLastPos();
pinchCenter = null;
});
hammer.on('doubletap', function (e) {
var c = rawCenter(e);
zoomAround(2, c.x, c.y);
});
};
</script>
<button onclick="zoomIn()">Zoom In</button>
<button onclick="zoomOut()">Zoom Out</button>
<div class="pinch-zoom-container">
<img id="pinch-zoom-image-id" class="pinch-zoom-image" onload="onLoad()"
src="https://hammerjs.github.io/assets/img/pano-1.jpg">
</div>
</div>
</body>
</html>

Preview window functionality similar to StackOverflow tags

I am trying to create an item preview(quickview) using javascript that changes it's orientation depending on the mouse position. I would like to have a functionality similar to the one existing in stackoverflow:
When an item is at the bottom of the page the preview window should change it's orientation(when the page is scrolled or browser window is resized). Currently it is shown below or above the cursor but it is not related to the actual cursor position(not related to the screen or browser window size). I am new to javascript and would appreciate any help.
Here is the code I am using as a starting point:
function AssignPosition(d) {
if(self.pageYOffset) {
rX = self.pageXOffset;
rY = self.pageYOffset;
}
else if(document.documentElement && document.documentElement.scrollTop) {
rX = document.documentElement.scrollLeft;
rY = document.documentElement.scrollTop;
}
else if(document.body && document.body.scrollTop) {
rX = document.body.scrollLeft;
rY = document.body.scrollTop;
}
if(document.all) {
cX += rX;
cY += rY;
}
var divHeight = d.style.height.replace(/px/, "");
divHeight = parseInt(divHeight);
if (cY < divHeight * 3 ) {
d.style.left = (cX+50) + "px";
d.style.top = (cY-10) + "px";
}
else {
cY = cY - divHeight;
d.style.left = (cX+50) + "px";
d.style.top = (cY-10) + "px";
}
}

Categories

Resources