I'm trying to write a very very simple zoom plugin that should have just a button to zoom in, zoom out, and the pan function to move the image around.
For now I've writte the part to zoom in and zoom out.
My problem is that I can't find a way to center the image inside the "zoombox".
This is my code so far:
$.fn.zoom = function() {
var img = this;
img.attr("style", "-ms-transform: scale(1); -ms-transform-origin: 0% 0%; -webkit-transform: scale(1); -webkit-transform-origin: 0% 0%").wrap('<div style="width: 400px; height: 400px; overflow: hidden;" class="zoombox" data-scale="1"></div>');
$("body").on("click.zoom", ".zoomin, .zoomout", function() {
if( $(this).hasClass("zoomin") ) {
var zoomFactor = (Number(img.parent().attr("data-scale")) + 0.1).toFixed(1);
} else {
var zoomFactor = (Number(img.parent().attr("data-scale")) - 0.1).toFixed(1);
}
img.parent().attr("data-scale", zoomFactor);
console.log(zoomFactor);
img.css({"-webkit-transform": "scale(" + zoomFactor + ")", "-ms-transform":"scale(" + zoomFactor + ")"});
});
};
Fiddle:
http://jsfiddle.net/xM7r4/1/
I know the style is not the best but I'm just trying to make it works without think about the style of the code.
How can I center the image inside the box, thinking that I will have to apply a pan effect later that will change the transform-origin values?
PS: I care about compatibility only on Chrome and IE9 for now.
edit for comment
You are correct. Here I've updated to work with transform-origin. It takes the dimensions of the containing div and divides by two (to get the centerpoint of the containing div) and passes these into the image's css transform-origin property:
http://jsfiddle.net/xM7r4/23/
I've tested with different dimensioned images, and it works.
original
You'll need to move the image using margin-left and margin-top depending on if you are zooming in or out.
http://jsfiddle.net/xM7r4/21/
Since you are increasing your image by a scale of 1%, you need to move the margins accordingly, negative for zoom-in, position for zoom-out.
$("body").on("click.zoom", ".zoomin, .zoomout", function() {
var imgWidth = $(img).width();
var imgHeight = $(img).height();
var scaleWidth = Math.floor(imgWidth * 0.01);
var scaleHeight = Math.floor(imgHeight * 0.01);
if( $(this).hasClass("zoomin") ) {
var zoomFactor = (Number(img.parent().attr("data-scale")) + 0.1).toFixed(1);
moveLeft -= scaleWidth;
moveTop -= scaleHeight;
} else {
var zoomFactor = (Number(img.parent().attr("data-scale")) - 0.1).toFixed(1);
moveLeft += scaleWidth;
moveTop += scaleHeight;
}
console.log(moveTop);
console.log(moveLeft);
img.parent().attr("data-scale", zoomFactor);
console.log(zoomFactor);
img.css({"-webkit-transform": "scale(" + zoomFactor + ")", "-ms-transform":"scale(" + zoomFactor + ")", "marginLeft": moveLeft, "marginTop": moveTop});
});
Related
I have an element I am zooming in (one button) and then rotating (another button)
When I zoom in on the element the scale gets updated, then when I go to rotate element the scale is not kept from the zoom (but it really is, I can see the value is maintained browser debug) but it zooms back out to the initial scale when rotate is clicked.
let spin = 0;
let zoom = 1;
$('#SpinRight').click(function(){
//I know for a fact scale keeps value here, but zooms back out to original size when I //click spin
spin += 25;
$('#element').css({'transform' : 'rotate(' + spin + 'deg'});
})
$('#Magnify').click(function(){
zoom += 0.1;
$('#element').css({'transform' : 'scale(' + zoom + ')'});
})
transform rules don't stack, so if you do:
transform: rotate(45deg);
transform: scale(0.5);
The scale transform replaces the rotate rule, it doesn't add to it. If you want both you need to specify them both in a single rule:
transform: rotate(45deg) scale(0.5);
You could keep track of the rotation and scale in css properties and then have a rule that applies them both. (Or just apply them the way you're already doing it, but do both for each update.)
const demo = document.querySelector('.demo');
let rotate = 0;
let scale = 1;
document.querySelector('.rotate').addEventListener('click', () => {
demo.style.setProperty('--rotate', `${rotate += 10}deg`);
});
document.querySelector('.scale').addEventListener('click', () => {
demo.style.setProperty('--scale', scale += 0.1);
});
.demo {
--rotate: 0;
background: skyblue;
width: 100px;
height: 100px;
transform: rotate(var(--rotate, 0)) scale(var(--scale, 1));
}
button {
position: relative; /* just so the buttons stay on top */
}
<div class="demo"></div>
<button class="rotate">Rotate</button>
<button class="scale">Scale</button>
I've got a custom cursor using some CSS - changes size on hover. What's the best way to snap the cursor back to the center of the mouse after the size increase?
Here's the fiddle: https://jsfiddle.net/fojn8bq9/
Here's the JavaScript I use to center the cursor initially:
const w = myCircle.offsetWidth / 2;
const h = myCircle.offsetHeight / 2;
const myMouse = new (function() {
this.follow = function(event) {
myCircle.style.left = event.pageX - w + 'px';
myCircle.style.top = event.pageY - h + 'px';
};
})();
What's the best way to fit ^it into my hover function?
$(document).ready(function(){
$("button").hover(function(){
$(myCircle).css({"height":"50px", "width":"50px"});
}, function(){
$(myCircle).css({"height":"25px", "width":"25px"});
});
});
You can just move the circle by half the difference between the old and new heights/widths:
$("button").hover(function(){
$(myCircle).css({"height":"50px", "width":"50px", transform: "translate(-12.5px, -12.5px)"});
}, function(){
$(myCircle).css({"height":"25px", "width":"25px", transform: ""});
});
});
Am trying to achieve this (built using webflow) animation and interaction when hovering on an element but am not able to do so. I've found this answer here but when I tried to refactor it with on hover function I still couldn't make it work.
Here's what I've tried.
// Maximum offset for image
var maxDeltaX = 50,
maxDeltaY = 50,
viewportWidth = 0,
viewportHeight = 0,
mouseX = 0,
mouseY = 0,
translateX = 0,
translateY = 0;
// Bind mousemove event to document
jQuery('.image-content-right').on('mousemove', function(e) {
// Get viewport dimensions
viewportWidth = document.documentElement.clientWidth,
viewportHeight = document.documentElement.clientHeight;
// Get relative mouse positions to viewport
// Original range: [0, 1]
// Should be in the range of -1 to 1, since we want to move left/right
// Transform by multipling by 2 and minus 1
// Output range: [-1, 1]
mouseX = e.pageX / viewportWidth * 2 - 1,
mouseY = e.pageY / viewportHeight * 2 - 1;
// Calculate how much to transform the image
translateX = mouseX * maxDeltaX,
translateY = mouseY * maxDeltaY;
jQuery('.cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-darktangirine').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
}).hover(function() {
jQuery('.cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-cyan').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
jQuery('.small-darktangirine').css('transform', 'translate(' + translateX + 'px, ' + translateY + 'px)');
})
It's a little bit clunky and not as smooth as what I want to achieve and also I would want it to go back to its original position when not hovered.
I'm not too sure how you'd really do much more to make that function a little smoother really considering its really depending on how often jQuery itself would execute its events. For now maybe I'd consider splitting all the code within your jQuery event declaration into their own respective functions. It'll be a lot easier and cleaner for you to work on :)
function animateElementOnMouseMove() {
// your translate code
}
function animateElementOnMouseHover() {
// your initial hover animation code
}
$('.image-content-right').on('mousemove', animateElementOnMouseMove)
.on('hover', animateElementOnMouseHover);
for it to return back to the position you had it at before you could either save an original untranslated position of each of the elements OR, you could save each of the translations into a count variable, then "undo" the translations after the element has become unfocused.
like:
var elementTranslateCountX = 0;
var elementTranslateCountY = 0;
// ON TRANSLATE
elementTranslateCountX += translateX;
elementTranslateCountY += translateY;
By the looks and feel of the webflow thing (if I understand your goal correctly) you want to be able to move your object by the full maxDeltaX/Y within the hover area. If that's the case, your math needs some adjustments: you need to define an origin (the center of the moving object most likely) and normalize to [-1, 1] the hover area around the origin. Placing the object in the dead center of the hover box simplifies calculations. I'm posting the code in a snippet, but it should be run on a full page because the coordinates are not correctly calculated. Funnily enough, if I run it on codepen, it works as expected on Chrome, but not on Safari. To avoid this issue you should wrap everything in a parent div and calculate coordinates relative to it
const page = document.getElementById("page-id");
const area = document.getElementById("area-id");
const box = document.getElementById("box-id");
// we want to move the object by 50px at most
const maxDeltaX = 50;
const maxDeltaY = 50;
let pageBox = page.getBoundingClientRect();
let pageTopLeft = {
x: pageBox.x,
y: pageBox.y
};
let areaBox = area.getBoundingClientRect();
let areaRange = {
w: areaBox.width / 2.0,
h: areaBox.height / 2.0
};
let boxBox = box.getBoundingClientRect();
let transformOrigin = {
x: boxBox.x + (boxBox.width / 2.0),
y: boxBox.y + (boxBox.height / 2.0)
};
// multipliers allow the full delta displacement within the hover area range
let multX = maxDeltaX / areaRange.w;
let multY = maxDeltaY / areaRange.h;
area.addEventListener("mousemove", onMove);
area.addEventListener("mouseleave", onLeave);
window.addEventListener("resize", onResize);
// mouse coords are computed wrt the wrapper top left corner and their distance from the object center is normalized
function onMove(e) {
let dx = (((e.clientX - pageTopLeft.x) - transformOrigin.x));
let dy = (((e.clientY - pageTopLeft.y) - transformOrigin.y));
box.style.transform = "translate3d(" + (dx * multX) + "px, " + (dy * multY) + "px, 0)";
/*
// or you can add some fancy rotation as well lol
let rotationDeg = Math.atan2(dy,dx) * (180/Math.PI);
let rotationString = "rotate(" + rotationDeg + "deg)";
box.style.transform = "translate3d(" + (dx * multX) + "px, " + (dy * multY) + "px, 0) " + rotationString;
*/
}
function onLeave(e) {
box.style.transform = "translate3d(0, 0, 0)";
}
function onResize(e) {
// redefine all the "let" variables
}
* {
margin: 0;
padding: 0;
}
.page {
position: relative;
width: 100%;
height: 100vh;
background-color: #ddd;
display: grid;
place-items: center;
}
.hover-area {
position: relative;
width: 50%;
height: 100%;
background-color: #888;
}
.box {
position: absolute;
left: calc(50% - 25px);
top: calc(50% - 25px);
width: 50px;
height: 50px;
border-radius: 25px;
background-image: linear-gradient(45deg, #000, #aaa);
transform: translate3d(0, 0, 0);
transition: all 0.2s;
will-change: transform;
}
<div id="page-id" class="page">
<div id="area-id" class="hover-area">
<div id="box-id" class="box" />
</div>
</div>
Note that is runs smoother on Chrome than on Safari. I'm not sure divs and css is the best way to go here for performance.
If I misunderstood the final result, explain more and I'll try to help.
And I want to get the width or distance or pixel that between the div and left side of of window/viewport.
And another width again between the div to the right side of the window.
I will use the width to create a left line and right line.
But I am poor in jQuery, I try offset but seems nothing happen.
So I back to 0 again so I didn't include fiddle here since I got nothing inside.
But I have attached with the image link as below, to explain my question.
Please help me on try to get the width, I can create the line myself.
Thank you.
var of = $(ele).offset(), // this will return the left and top
left = of.left, // this will return left
right = $(window).width() - left - $(ele).width() // you can get right by calculate
Maybe this can help you.
After all, .width() isn't the only answer, like innerWidth() or outerWidth()
There is two options
One is you can use red line as image and you can place the div over the red line.
Second one,
If you want to calculate:
Left div width = parent div width - child div offset;
Right div width = parent div width - child div offset + child div width;
var parentdiv = document.getElementById("ParentDivID");
var parentWidth = parentdiv.offsetWidth;
var childdiv = document.getElementById("childDivID");
var childWidth = childdiv.offsetLeft;
This is easier to do with POJ (plain old javascript). Get the position of the element on the screen. Then evaluate its left property. That will be the width of your left line. Then subtract its right property from the width of the screen. That will be the width of your right line.
var x = document.getElementById('myDiv').getBoundingClientRect();
var myLeftLineWidth = x.left;
var myRightLineWidth = screen.width - x.right;
For more information see this post.
If you want the width of the window instead of the screen, change screen.width to window.innerWidth. If you don't want the scrollbar, etc. to be included in the width, use document.documentElement.clientWidth. (For more info on these, see this.)
We can work out that where the box starts with .offset().
Next, we can work out where the box ends with .offset() + .width().
We now know where our box sits on the x-axis.
Now let's see what we have to the left of our box with .left which can run on our .offset().
We've now worked out how much space there is to the left and how wide our box is.
Finally, we can put what we've worked out together, we can get the page width $(window).width() and minus what there is to the left of our box (stage 2) and the width of our box (stage 1) and that will only leave what is to the right of our box.
That's the theory anyway now let's have a look at some code. You'll see I'm working out all the bits from the theory and then adding some visual representation.
calcSizes = function() {
var boxPos = $(".box").offset(),
toLeft = boxPos.left,
toRight = $(window).width() - ($(".box").width() + toLeft);
$(".left").width(toLeft + "px");
$(".right").width(toRight + "px");
console.log("Right: " + toRight + "px");
console.log("Left: " + toLeft + "px");
console.log("Width: " + $(".box").width() + "px");
console.log(
$(window).width() + "px = " +
toRight + "px + " +
toLeft + "px + " +
$(".box").width() + "px"
);
console.log(" ");
}
calcSizes();
body {
margin: 0
}
.box,
.indicator {
padding: 10px 0;
text-align: center
}
.box {
width: 100px;
background: #FF5722;
margin-left: 60%
}
.indicator {
background: repeating-linear-gradient( 45deg, #F44336, #F44336 10px, #D32F2F 10px, #D32F2F 20px);
overflow: hidden;
transform: translatey(-100%);
opacity: .8
}
.left {
float: left
}
.right {
float: right
}
button {
position: fixed;
top: 55px;
left: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box">BOX</div>
<div class="left indicator">
LEFT
</div>
<div class="right indicator">
RIGHT
</div>
<button onclick="calcSizes()">
Recalculate
</button>
Hope this makes sense and helps you with your project.
You can do that with JavaScript, no need for jQuery:
var mydiv = document.getElementById('mydiv');
var offset = mydiv.getBoundingClientRect();
var offsetRight = document.documentElement.clientWidth - offset.right;
var offsetLeft = offset.left;
JSFiddle
Is there a technique to resize an image over a given time interval?
What I want to do is have an image and when the mouse rolls overs it, it should resize the image making it larger. All I can find are simple rollover scripts that instantly resize the image. I want to do it over a period of about a second.
And as a must it cannot lag and destroy the visual experience. I am looking for an approach in javascript, jQuery, or HTML5 if it's possible; other suggestions appreciated but no flash.
It's very easy with CSS3 Transitions:
.myImg
{
width: 200px;
transition-duration: 1s;
-webkit-transition-duration: 1s;
}
.myImg:hover
{
width: 300px;
}
Demo: jsfiddle.net/yyDd4
You can do it in jQuery in this way.
var factor = 2;
$('#foo').mouseover(function() {
$(this).animate({
top: '-=' + $(this).height() / factor,
left: '-=' + $(this).width() / factor,
width: $(this).width() * factor
});
});
and the other techniques are here.
You can do this in plain javascript, though animation is always surprisingly complicated, especially if you want the image to shrink back after the mouse moves off it. Making an object to store the state is possibly the best solution and is also quite adaptable (other images, other types of animation).
http://jsfiddle.net/VceD9/6/
new GrowingImage('myImage', 2, 1000);
function GrowingImage(id, factor, duration) {
var el = document.getElementById(id),
originalWidth = el.offsetWidth,
originalHeight = el.offsetHeight,
timer,
stage = 0,
frameRate = 17,
maxStage = duration / frameRate;
el.onmouseover = function () {
animate(1);
};
el.onmouseout = function () {
animate(-1);
};
function animate(direction) {
clearInterval(timer);
timer = setInterval(function() {
stage += direction;
if (stage <= 0) {
stage = 0;
clearInterval(timer);
} else if (stage >= maxStage) {
stage = maxStage;
clearInterval(timer);
}
var scale = 1 + (factor - 1) * stage / maxStage;
el.style.width = originalWidth * scale + 'px';
el.style.height = originalHeight * scale + 'px';
}, frameRate);
}
}
If exact timing is important to you, you may need to adjust this so that it keeps track of the amount of time that the current animation has been running.