Background
The below code creates a square box <div class="box"> and adds an animation of different sized circles.
At the moment, the script creates an animation across the whole page, but I’m trying to apply the animation script specifically to the square box <div class="box">.
I’ve tried changing the script document.body.append(circle) to document.getElementsByClassName("box")[0].appendChild(circle) with no success, the animation continues to apply across the whole page.
Question
What code changes are needed to apply the Script animation specifically to <div class="box"> ?
Code
<html>
<h1>Heading 1</h1>
<div class="box">
<h2>Heading 2</h2>
<p>This is some text inside a div element.</p>
</div>
<p>This is some text outside the div element.</p>
</html>
<style>
.circle {
position: absolute;
border-radius: 100%;
}
.box {
height: 500px;
width: 500px;
border: 5px outset red;
text-align: center;
}
</style>
<script>
const colors = ["#000000"];
const numCircles = 50;
const circles = [];
for (let i = 0; i < numCircles; i++) {
let circle = document.createElement("div");
circle.classList.add("circle");
circle.style.background = colors[Math.floor(Math.random() * colors.length)];
circle.style.left = `${Math.floor(Math.random() * 100)}vw`;
circle.style.top = `${Math.floor(Math.random() * 100)}vh`;
circle.style.transform = `scale(${Math.random()})`;
circle.style.width = `${Math.random()}em`;
circle.style.height = circle.style.width;
circles.push(circle);
document.body.append(circle);
}
circles.forEach((el, i, ra) => {
let to = {
x: Math.random() * (i % 10 === 0 ? -10 : 10),
y: Math.random() * 10
};
let anim = el.animate(
[
{ transform: "translate(0, 0)" },
{ transform: `translate(${to.x}rem, ${to.y}rem)` }
],
{
duration: (Math.random() + 1) * 2000,
direction: "alternate",
fill: "both",
iterations: Infinity,
easing: "ease-in-out"
}
);
});
</script>
Image
The problem is in
circle.style.left = `${Math.floor(Math.random() * 100)}vw`;
circle.style.top = `${Math.floor(Math.random() * 100)}vh`;
vw and vh use the full page width and full page height.
Change them to % AND replace document.body.append(circle); with document.querySelector('.box').append(circle). Also make sure the .box has position:relative to make sure the absolute positioning stays inside the box.
Result
step1: changing the script document.body.append(circle) to document.getElementsByClassName("box")[0].appendChild(circle)
step2: add below css to .box;
{
transform: scale(1);
overflow: hidden;
}
Result
Related
I am having a little bit of trouble with my CSS, as when I change the default zoom (Command + on Mac) of the browser it causes the below image.
When it is at 100% viewport on chrome, it is supposed to look like the below where it fits perfectly in the black box.
My html code is below. For the sake of simplicity, I have only included the container and the tag to show where I wrote it.
<body>
<div class="container">
<div class="container-bars"></div>
</div>
</body>
My CSS code is below:
body {
background-color: rgb(89, 87, 87);
margin-right: auto;
margin-left: auto;
.container {
background-color: rgb(37, 35, 35);
height: 80%;
position: absolute;
width: 60%;
left: 500px;
top: 150px;
}
.bars {
float: left;
border: 1px solid rgb(232, 10, 10);
background-color: rgb(218, 215, 215);
}
The .container is the black box in the background and the body is the grey background. Finally, .bars just represents the CSS for each bar. I am creating a visualizer so the when adding the bars, I am using javascript. The code for adding the bars is below.
const container = document.querySelector('.container');
function add_bars(input) {
const Div = document.createElement('div');
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const barWidth = containerWidth / inputval - 2;
Div.className = 'bars';
//prettier-ignore
Div.style.height = `${((containerHeight / 100) * input) - 2}px`;
Div.style.width = `${barWidth}px`;
containerBars.append(Div);
}
The input in this case is how large the size of the array is which I am dividing in order to split the width of each bar equally. Any help would be appreciated as to why it exceeds the container when I change how "zoomed" in the browser is. Thanks!
function for_loop(array) {
resetArray();
for (let i = 0; i < array.length; i++) {
add_bars(array[i]);
}
}
When you use % or vh vw in css and change zoom the elements will change their sizes. Try a console.log(document.querySelector(".container")) after and before changing zoom and you will see a different value. In order to avoid that behavior you have to use fixed sizes in your elements. For example:
.container {
background-color: rgb(37, 35, 35);
position: absolute;
height: 600px; // pixels, not % or vh
width: 800px;// the same
left: 150px;
top: 150px;
}
Also, when you make zoom out the problem persist. In order to avoid that just add the next:
* {
box-sizing: border-box;
}
And there is no need to substract 2 px in add_bars function:
...
const barWidth = containerWidth / inputval;
...
Div.style.height = `${((containerHeight / 100) * input)}px`;
...
I have a red container which initially is at bottom right of black container. I have a scale function that gradually scales the container. I want to make the bottom right position of red container to be fixed and scale it towards top left. How can I do that?
const box = document.getElementById("box")
const initHeight = 200
const initWidth = 200
const centerX = initWidth / 2
const centerY = initHeight / 2
function transform(scale, translate) {
if (translate) {
translate[0] = -centerX + translate[0]
translate[1] = -centerY + translate[1]
}
box.style.transform = `scale(${scale})${
translate ? ` translate(${translate.map((x) => x + "px").toString()})` : ""
}`
}
let initX = initWidth
let initY = initHeight
let scaleVal = 0.5
transform(scaleVal, [initX, initY])
function scale() {
scaleVal = scaleVal + 0.01
transform(scaleVal, [
initX - scaleVal * initWidth,
initY - scaleVal * initHeight
])
if (scaleVal <= 1) {
setTimeout(() => {
requestAnimationFrame(scale)
}, 50)
}
}
scale()
* {
box-sizing: border-box;
}
.box {
height: 200px;
width: 200px;
background-color: black;
position: absolute;
}
.box:nth-child(2) {
background-color: red;
}
<div id="app">
<div class="box"></div>
<div class="box" id="box"></div>
</div>
To lock the bottom right corner of the red box to the bottom right of the black box this snippet does two things: positions red box right bottom relative to the parent app container and sets the transform-origin to that spot too (normally transform origin is at the center of an element). It then uses a CSS animation to expand the red box and contract it again using scale.
This method does not need JS as it is a simple scaling transform, but of course some of the subtleties of the original transformations are lost because of tying the corner down.
* {
box-sizing: border-box;
}
body {
position: relative;
width: 100vw;
height: 100vh;
}
#app {
position: absolute;
height: 200px;
width: 200px;
}
.box:nth-child(1) {
height: 200px;
width: 200px;
background-color: black;
position: absolute;
top: 0;
left: 0;
}
#box {
background-color: red;
width: 100px;
height: 100px;
position: absolute;
bottom: 0;
right: 0;
transform-origin: right bottom;
animation: scale 5s 1 ease-in-out;
}
#keyframes scale {
0% {
transform: scale(1);
}
50% {
transform: scale(2);
}
100% {
transform: scale(1);
}
}
<div id="app">
<div class="box"></div>
<div class="box" id="box"></div>
</div>
Okay so I finally figured it out,
const box = document.getElementById("box")
let scale = 0
const initWidth = 50
const initHeight = 50
function fixed(num, fix = 1) {
return Number(parseFloat(num).toFixed(fix))
}
function scaleBox() {
const [x, y] = [
fixed((initWidth - scale * initWidth) / 2),
fixed((initHeight - scale * initHeight) / 2)
]
box.style.transform = `translate(${x}px, ${y}px) scale(${scale})`
scale = scale + 0.1
if (scale < 1) {
setTimeout(() => {
requestAnimationFrame(scaleBox)
}, 500)
}
}
scaleBox()
* {
box-sizing: border-box;
}
.box {
height: 50px;
width: 50px;
background-color: black;
position: absolute;
}
.box:nth-child(2) {
background-color: red;
transform: translate(0, 0) scale(0);
}
<div id="app">
<div class="box"></div>
<div class="box" id="box"></div>
</div>
Explanation
The trick is to translate the container in such a way that when its scaled after the translation, it always places itself in the bottom right of purple container.
To figure out the translation amount, let's first scale the container to 0.5 without any translation. It looks like this,
As you can see the container's width is 25 as 0.5(scale) * 50(init_width)=25 and position from container from all sides(top left, bottom left, top right, bottom right) will be (25/2, 25/2)=(12.5,12.5) since the container is scaled equally from all sides.
Since the position from bottom right is (12.5,12.5), we need to translate the container to (+12.5,+12.5) and then scale it to exactly place it at bottom right.
You can achieve many things using display:flex, it's great!
This is how I would approach your problem:
const handleClick = () => {
const blackDiv = document.getElementById("black-div");
const redDiv = document.getElementById("red-div");
let widthRatio = 0;
let heightRatio = 0;
const scaleUpTimer = setInterval(() => {
if (widthRatio === 1 || heightRatio === 1) clearInterval(scaleUpTimer);
widthRatio = redDiv.offsetWidth / blackDiv.offsetWidth;
heightRatio = redDiv.offsetHeight / blackDiv.offsetHeight;
redDiv.style.width = widthRatio * 100 + 2 + "%";
redDiv.style.height = heightRatio * 100 + 2 + "%";
}, 10);
};
#black-div {
width: 200px;
height: 200px;
background-color: rgb(0, 0, 0);
display: flex;
align-items: flex-end;
justify-content: flex-end;
}
#red-div {
background-color: red;
width: 50%;
height: 50%;
}
<div id='black-div'>
<div id='red-div' onclick={handleClick()}></div>
</div>
EDIT: I used onclick here but obviously you would have to handle the situations where someone clicks the red square and its already scaled up to avoid setting unnecessary timers. Or you could just call the function directly, without having to click anything.
Background
The below code adds to a DIV an animation of different sized floating circles.
The animation works well on desktop browsers, however, on an iPad tablet, all it shows is a static image, no animation.
Question
What is preventing the code from animating on tablet compared with desktop?
How do I convert the animation in Java Script to an equivalent CSS animation?
const colors = ["#000000"];
const numCircles = 50;
const circles = [];
for (let i = 0; i < numCircles; i++) {
let circle = document.createElement("div");
circle.classList.add("circle");
circle.style.background = colors[Math.floor(Math.random() * colors.length)];
circle.style.left = `${Math.floor(Math.random() * 100)}vw`;
circle.style.top = `${Math.floor(Math.random() * 100)}vh`;
circle.style.transform = `scale(${Math.random()})`;
circle.style.width = `${Math.random()}em`;
circle.style.height = circle.style.width;
circles.push(circle);
document.body.append(circle);
}
circles.forEach((el, i, ra) => {
let to = {
x: Math.random() * (i % 10 === 0 ? -10 : 10),
y: Math.random() * 10
};
let anim = el.animate(
[
{ transform: "translate(0, 0)" },
{ transform: `translate(${to.x}rem, ${to.y}rem)` }
],
{
duration: (Math.random() + 1) * 2000,
direction: "alternate",
fill: "both",
iterations: Infinity,
easing: "ease-in-out"
}
);
});
.circle {
position: absolute;
border-radius: 100%;
}
.box {
height: 500px;
width: 500px;
border: 5px outset red;
text-align: center;
}
<h1>Heading 1</h1>
<div class="box">
<h2>Heading 2</h2>
<p>This is some text inside a div element.</p>
</div>
<p>This is some text outside the div element.</p>
Image
What is preventing the code from animating on tablet compared with desktop?
Your JavaScript code is using Element.animate() which is not currently widely supported by Safari for macOS or Safari for iOS (the minimum version is iOS 13.4, which was only released 2 weeks ago).
How do I convert the animation in Java Script to an equivalent CSS animation?
You'll still need the script to create all of the <div> elements at random positions, but the animate() your code uses can be converted to a declarative CSS animation, like so:
You can run this snippet:
Note that I needed add px and s units to the --to-x, --to-y, and duration values, and I had to disable the circle.style.transform = 'scale' CSSOM rule because they were overriding the declarative CSS (the transform: scale() isn't needed anyway because the circle.style.width/height are also randomized.
const colors = ["#000000"];
function createCircles( count ) {
const circles = [];
for (let i = 0; i < count; i++) {
const circle = document.createElement("div");
circle.classList.add("circle");
circle.style.background = colors[Math.floor(Math.random() * colors.length)];
circle.style.left = `${Math.floor(Math.random() * 100)}vw`;
circle.style.top = `${Math.floor(Math.random() * 100)}vh`;
/* circle.style.transform = `scale(${Math.random()})`; <-- This is now disabled */
circle.style.width = `${Math.random()}em`;
circle.style.height = circle.style.width;
const toX = Math.random() * (i % 10 === 0 ? -100 : 100);
const toY = Math.random() * 100;
const dur = (Math.random() + 1) * 2;
circle.style.setProperty( '--to-x', toX.toFixed(0) + 'px' );
circle.style.setProperty( '--to-y', toY.toFixed(0) + 'px' );
circle.style.setProperty( '--duration', dur.toFixed(2) + 's' );
circles.push(circle);
}
return circles;
}
window.addEventListener( 'DOMContentLoaded', setup );
function setup() {
const circles = createCircles(50);
for( const circle of circles ) {
document.body.append( circle );
}
}
.circle {
position: absolute;
border-radius: 100%;
animation-name: myAnimation;
animation-duration: var(--duration);
animation-direction: alternate;
animation-fill-mode: both;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
.box {
height: 500px;
width: 500px;
border: 5px outset red;
text-align: center;
}
#keyframes myAnimation {
from {
transform: translate( 0, 0 );
}
to {
transform: translate( var(--to-x), var(--to-y) );
}
}
<h1>Heading 1</h1>
<div class="box">
<h2>Heading 2</h2>
<p>This is some text inside a div element.</p>
</div>
<p>This is some text outside the div element.</p>
This also works without any JavaScript if you render the random initial circles into your HTML directly (e.g. using PHP):
<html>
<head>
</head>
<body>
<h1>Heading 1</h1>
<div class="box">
<h2>Heading 2</h2>
<p>This is some text inside a div element.</p>
</div>
<p>This is some text outside the div element.</p>
<?php for( $i = 0; $i < 50; $i++ ) { ?>
<div class="circle" style="left: <?= rand( 0, 100 ) ?>px; top: <?= rand( 0, 100 ) ?>px; width: <?= rand( 0, 50 ) ?>px; height: <?= rand( 0, 50 ) ?>px; --to-x: <?= rand( 0, 50 ) ?>px; --to-y: <?= rand( 0, 50 ) ?>px; --duration: <?= rand( 0, 5 ) ?>s;"></div>
<?php endfor; ?>
</body>
</html>
Live example: https://jsfiddle.net/b8vLg0ny/
It's possible to use the CSS scale and translate functions to zoom into element.
Take this example, of 4 boxes in a 2x2 grid.
HTML:
<div id="container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
</div>
</div>
CSS:
* { margin: 0; }
body, html { height: 100%; }
#container {
height: 100%;
width: 50%;
margin: 0 auto;
}
#zoom-container {
height: 100%;
width: 100%;
transition: all 0.2s ease-in-out;
}
.box {
float: left;
width: 50%;
height: 50%;
color: white;
text-align: center;
display: block;
}
.red { background: red; }
.blue { background: blue; }
.green { background: green; }
.black { background: black; }
JavaScript:
window.zoomedIn = false;
$(".box").click(function(event) {
var el = this;
var zoomContainer = $("#zoom-container");
if (window.zoomedIn) {
console.log("resetting zoom");
zoomContainer.css("transform", "");
$("#container").css("overflow", "auto");
window.zoomedIn = false;
} else {
console.log("applying zoom");
var top = el.offsetTop;
var left = el.offsetLeft - 0.25*zoomContainer[0].clientWidth;
var translateY = 0.5*zoomContainer[0].clientHeight - top;
var translateX = 0.5*zoomContainer[0].clientWidth - left;
$("#container").css("overflow", "scroll");
zoomContainer.css("transform", "translate(" + 2 * translateX + "px, " + 2 * translateY + "px) scale(2)");
window.zoomedIn = true;
}
});
By controlling the value of translateX and translateY, you can change how the zooming works.
The initial rendered view looks something like this:
Clicking on the A box will zoom you in appropriately:
(Note that clicking D at the end is just showing the reset by zooming back out.)
The problem is: zooming to box D will scale the zoom container such that scrolling to the top and left doesn't work, because the contents overflow. The same happens when zooming to boxes B (the left half is cropped) and C (the top half is cropped). Only with A does the content not overflow outside the container.
In similar situations related to scaling (see CSS3 Transform Scale and Container with Overflow), one possible solution is to specify transform-origin: top left (or 0 0). Because of the way the scaling works relative to the top left, the scrolling functionality stays. That doesn't seem to work here though, because it means you're no longer repositioning the contents to be focused on the clicked box (A, B, C or D).
Another possible solution is to add a margin-left and a margin-top to the zoom container, which adds enough space to make up for the overflowed contents. But again: the translate values no longer line up.
So: is there a way to both zoom in on a given element, and overflow with a scroll so that contents aren't cropped?
Update: There's a rough almost-solution by animating scrollTop and scrollLeft, similar to https://stackoverflow.com/a/31406704/528044 (see the jsfiddle example), but it's not quite a proper solution because it first zooms to the top left, not the intended target. I'm beginning to suspect this isn't actually possible, because it's probably equivalent to asking for scrollLeft to be negative.
Why not just to reposition the TransformOrigin to 0 0 and to use proper scrollTop/scrollLeft after the animation?
https://jsfiddle.net/b8vLg0ny/7/
Updated: https://jsfiddle.net/b8vLg0ny/13/
If you do not need the animation, the TransformOrigin can always stays 0 0 and only the scrolling is used to show the box.
To make the animation less jumpy use transition only for transform porperty, otherwise the transform-origin gets animated also. I have edited the example with 4x4 elements, but I think it makes sense to zoom a box completely into view, thats why I changed the zoom level. But if you stay by zoom level 2 and the grid size 15x15 for instance, then with this approach really precise origin should be calculated for transform, and then also the correct scrolling.
Anyway I don't know, if you find this approach useful.
Stack snippet
var zoomedIn = false;
var zoomContainer = $("#zoom-container");
$(".box").click(function(event) {
var el = this;
if (zoomedIn) {
zoomContainer.css({
transform: "scale(1)",
transformOrigin: "0 0"
});
zoomContainer.parent().scrollTop(0).scrollLeft(0);
zoomedIn = false;
return;
}
zoomedIn = true;
var $el = $(el);
animate($el);
zoomContainer.on('transitionend', function(){
zoomContainer.off('transitionend');
reposition($el);
})
});
var COLS = 4, ROWS = 4,
COLS_STEP = 100 / (COLS - 1), ROWS_STEP = 100 / (ROWS - 1),
ZOOM = 4;
function animate($box) {
var cell = getCell($box);
var col = cell.col * COLS_STEP + '%',
row = cell.row * ROWS_STEP + '%';
zoomContainer.parent().css('overflow', 'hidden');
zoomContainer.css({
transition: 'transform 0.2s ease-in-out',
transform: "scale(" + ZOOM + ")",
transformOrigin: col + " " + row
});
}
function reposition($box) {
zoomContainer.css({
transition: 'none',
transform: "scale(" + ZOOM + ")",
transformOrigin: '0 0'
});
zoomContainer.parent().css('overflow', 'auto');
$box.get(0).scrollIntoView();
}
function getCell ($box) {
var idx = $box.index();
var col = idx % COLS,
row = (idx / ROWS) | 0;
return { col: col, row: row };
}
* { margin: 0; }
body, html { height: 100%; }
#container {
height: 100%;
width: 50%;
margin: 0 auto;
overflow: hidden;
}
#zoom-container {
height: 100%;
width: 100%;
will-change: transform;
}
.box {
float: left;
width: 25%;
height: 25%;
color: white;
text-align: center;
}
.red { background: red; }
.blue { background: blue; }
.green { background: green; }
.black { background: black; }
.l { opacity: .3 }
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<div id="container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
<div class="box red l">E</div>
<div class="box blue l">F</div>
<div class="box green l">G</div>
<div class="box black l">H</div>
<div class="box red">I</div>
<div class="box blue">J</div>
<div class="box green">K</div>
<div class="box black">L</div>
<div class="box red l">M</div>
<div class="box blue l">N</div>
<div class="box green l">O</div>
<div class="box black l">P</div>
</div>
</div>
I'm answering my own question, since I'm fairly confident that it's actually not possible with the given requirements. At least not without some hackery that would cause problems visually, e.g., jumpy scrolling by animating scrollTop after switching transform-origin to 0, 0 (which removes the cropping by bringing everything back into the container).
I'd love for someone to prove me wrong, but it seems equivalent to asking for scrollLeft = -10, something that MDN will tell you is not possible. ("If set to a value less than 0 [...], scrollLeft is set to 0.")
If, however, it's acceptable to change the UI from scrolling, to zooming and dragging/panning, then it's achievable: https://jsfiddle.net/jegn4x0f/5/
Here's the solution with the same context as my original problem:
HTML:
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<button id="zoom-out">Zoom out</button>
<div id="container">
<div id="inner-container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
</div>
</div>
</div>
JavaScript:
//
// credit for the approach goes to
//
// https://stackoverflow.com/questions/35252249/move-drag-pan-and-zoom-object-image-or-div-in-pure-js#comment58224460_35253567
//
// and the corresponding example:
//
// https://jsfiddle.net/j8kLz6wm/1/
//
// in a real-world setting, you
// wouldn't keep this information
// on window. this is just for
// the demonstration.
window.zoomedIn = false;
// stores the initial translate values after clicking on a box
window.translateY = null;
window.translateX = null;
// stores the incremental translate values based on
// applying the initial translate values + delta
window.lastTranslateY = null;
window.lastTranslateX = null;
// cursor position relative to the container, at
// the time the drag started
window.dragStartX = null;
window.dragStartY = null;
var handleDragStart = function(element, xCursor, yCursor) {
window.dragStartX = xCursor - element.offsetLeft;
window.dragStartY = yCursor - element.offsetTop;
// disable transition animations, since we're starting a drag
$("#zoom-container").css("transition", "none");
};
var handleDragEnd = function() {
window.dragStartX = null;
window.dragStartY = null;
// remove the individual element's styling for transitions
// which brings back the stylesheet's default of animating.
$("#zoom-container").css("transition", "");
// keep track of the translate values we arrived at
window.translateY = window.lastTranslateY;
window.translateX = window.lastTranslateX;
};
var handleDragMove = function(xCursor, yCursor) {
var deltaX = xCursor - window.dragStartX;
var deltaY = yCursor - window.dragStartY;
var translateY = window.translateY + (deltaY / 2);
// the subtracted value here is to keep the letter in the center
var translateX = window.translateX + (deltaX / 2) - (0.25 * $("#inner-container")[0].clientWidth);
// fudge factor, probably because of percentage
// width/height problems. couldn't really trace down
// the underlying cause. hopefully the general approach
// is clear, though.
translateY -= 9;
translateX -= 4;
var innerContainer = $("#inner-container")[0];
// cap all values to prevent infinity scrolling off the page
if (translateY > 0.5 * innerContainer.clientHeight) {
translateY = 0.5 * innerContainer.clientHeight;
}
if (translateX > 0.5 * innerContainer.clientWidth) {
translateX = 0.5 * innerContainer.clientWidth;
}
if (translateY < -0.5 * innerContainer.clientHeight) {
translateY = -0.5 * innerContainer.clientHeight;
}
if (translateX < -0.5 * innerContainer.clientWidth) {
translateX = -0.5 * innerContainer.clientWidth;
}
// update the zoom container's translate values
// based on the original + delta, capped to the
// container's width and height.
$("#zoom-container").css("transform", "translate(" + (2*translateX) + "px, " + (2*translateY) + "px) scale(2)");
// keep track of the updated values for the next
// touchmove event.
window.lastTranslateX = translateX;
window.lastTranslateY = translateY;
};
// Drag start -- touch version
$("#container").on("touchstart", function(event) {
if (!window.zoomedIn) {
return true;
}
var xCursor = event.originalEvent.changedTouches[0].clientX;
var yCursor = event.originalEvent.changedTouches[0].clientY;
handleDragStart(this, xCursor, yCursor);
});
// Drag start -- mouse version
$("#container").on("mousedown", function(event) {
if (!window.zoomedIn) {
return true;
}
var xCursor = event.clientX;
var yCursor = event.clientY;
handleDragStart(this, xCursor, yCursor);
});
// Drag end -- touch version
$("#inner-container").on("touchend", function(event) {
if (!window.zoomedIn) {
return true;
}
handleDragEnd();
});
// Drag end -- mouse version
$("#inner-container").on("mouseup", function(event) {
if (!window.zoomedIn) {
return true;
}
handleDragEnd();
});
// Drag move -- touch version
$("#inner-container").on("touchmove", function(event) {
// prevent pull-to-refresh. could be smarter by checking
// if the page's scroll y-offset is 0, and even smarter
// by checking if we're pulling down, not up.
event.preventDefault();
if (!window.zoomedIn) {
return true;
}
var xCursor = event.originalEvent.changedTouches[0].clientX;
var yCursor = event.originalEvent.changedTouches[0].clientY;
handleDragMove(xCursor, yCursor);
});
// Drag move -- click version
$("#inner-container").on("mousemove", function(event) {
// prevent pull-to-refresh. could be smarter by checking
// if the page's scroll y-offset is 0, and even smarter
// by checking if we're pulling down, not up.
event.preventDefault();
// if we aren't dragging from anywhere, don't move
if (!window.zoomedIn || !window.dragStartX) {
return true;
}
var xCursor = event.clientX;
var yCursor = event.clientY;
handleDragMove(xCursor, yCursor);
});
var zoomInTo = function(element) {
console.log("applying zoom");
var top = element.offsetTop;
// the subtracted value here is to keep the letter in the center
var left = element.offsetLeft - (0.25 * $("#inner-container")[0].clientWidth);
var translateY = 0.5 * $("#zoom-container")[0].clientHeight - top;
var translateX = 0.5 * $("#zoom-container")[0].clientWidth - left;
$("#container").css("overflow", "scroll");
$("#zoom-container").css("transform", "translate(" + (2*translateX) + "px, " + (2*translateY) + "px) scale(2)");
window.translateY = translateY;
window.translateX = translateX;
window.zoomedIn = true;
}
var zoomOut = function() {
console.log("resetting zoom");
window.zoomedIn = false;
$("#zoom-container").css("transform", "");
$("#zoom-container").css("transition", "");
window.dragStartX = null;
window.dragStartY = null;
window.dragMoveJustHappened = null;
window.translateY = window.lastTranslateY;
window.translateX = window.lastTranslateX;
window.lastTranslateX = null;
window.lastTranslateY = null;
}
$(".box").click(function(event) {
var element = this;
var zoomContainer = $("#zoom-container");
if (!window.zoomedIn) {
zoomInTo(element);
}
});
$("#zoom-out").click(function(event) {
zoomOut();
});
CSS:
* {
margin: 0;
}
body,
html {
height: 100%;
}
#container {
height: 100%;
width: 50%;
margin: 0 auto;
}
#inner-container {
width: 100%;
height: 100%;
}
#zoom-container {
height: 100%;
width: 100%;
transition: transform 0.2s ease-in-out;
}
.box {
float: left;
width: 50%;
height: 50%;
color: white;
text-align: center;
display: block;
}
.red {
background: red;
}
.blue {
background: blue;
}
.green {
background: green;
}
.black {
background: black;
}
I pieced this together from another question (Move (drag/pan) and zoom object (image or div) in pure js), where the width and height are being changed. That doesn't quite apply in my case, because I need to zoom into a specific element on the page (with a lot boxes than in a 2x2 grid). The solution from that question (https://jsfiddle.net/j8kLz6wm/1/) shows the basic approach in pure JavaScript. If you have jQuery available, you can probably just use jquery.panzoom.
Update
I got stuck on scroll bars not showing all the time, so I need to investigating that part, so that code is commented out and instead I use a delay to move the clicked box into view.
Here is my fiddle demo, which I use to play with, to figure out how to solve the scroll bar issue.
Side note: In a comment made by #AVAVT, I would like to link to his post here, as that might help someone else, which I find as an interesting alternative in some cases.
(function(zoomed) {
$(".box").click(function(event) {
var el = this, elp = el.parentElement;
if (zoomed) {
zoomed = false;
$("#zoom-container").css({'transform': ''});
} else {
zoomed = true;
/* this zooms correct but show 1 or none scroll for B,C,D so need to figure out why
var tro = (Math.abs(elp.offsetTop - el.offsetTop) > 0) ? 'bottom' : 'top';
tro += (Math.abs(elp.offsetLeft - el.offsetLeft) > 0) ? ' right' : ' left';
$("#zoom-container").css({'transform-origin': tro, 'transform': 'scale(2)'});
*/
$("#zoom-container").css({'transform-origin': '0 0', 'transform': 'scale(2)'});
/* delay needed before scroll into view */
setTimeout(function() {
el.scrollIntoView();
},250);
}
});
})();
* { margin: 0; }
body, html { height: 100%; }
#container {
height: 100%;
width: 50%;
overflow: auto;
margin: 0 auto;
}
#zoom-container {
height: 100%;
width: 100%;
transition: all 0.2s ease-in-out;
}
.box {
float: left;
width: 50%;
height: 50%;
color: white;
text-align: center;
display: block;
}
.red {
background: red;
}
.blue {
background: blue;
}
.green {
background: green;
}
.black {
background: black;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<div id="container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
</div>
</div>
I am working on an experimental art site that involves a grid of images each scaled to the size of the viewport. The background for the page body should be one stretched image and it was working at one point but has stopped functioning. The background color shows and the image simply doesn't. However if the background-size CSS is turned off in Chrome inspector it shows up at it's own size positioned top and left...
I am guessing that having body contain a box which amounts to 5 viewports wide and 5 viewports tall is messing with what the body element's size is? Though allowing bg image to repeat didn't make it show up...
I resolved this by grabbing the screen width and height in the JavaScript and then setting background-size to those values like so:
$('body,html').css('background-size',the_width + 'px ' + the_height+'px');
But my question remains- why did the background-size:100% 100% stop working?
The page HTML looks like this only with 25 total divs and images.
<body>
<div id="box">
<div class="full" id="full_10">
<img src="home_tiles/10.jpg" width="800" height="600" title="10">
</div>
<div class="full" id="full_11">
<img src="home_tiles/11.jpg" width="800" height="600" title="11">
</div>
</div>
</body>
The CSS looks like this
* { padding: 0; margin: 0; }
html, body {
overflow:hidden;
background-color: #FF0000;
background-size: 100% 100%;
background-image:url(home_tiles/edge.jpg);
background-repeat:no-repeat;
}
img { display: block; }
#box {
position:absolute;
}
.full {
float:left;
}
The JavaScript sizing the images is
$(function() {
$('.full').hide();
var win = $(window),
fullscreen = $('.full'),
image = fullscreen.find('img'),
imageWidth = image.width(),
imageHeight = image.height(),
imageRatio = imageWidth / imageHeight;
var the_width;
var the_height;
var left_limit;
var right_limit;
function resizeImage() {
var winWidth = win.width(),
winHeight = win.height(),
winRatio = winWidth / winHeight;
if(winRatio > imageRatio) {
the_width = winWidth;
the_height = Math.round(winWidth / imageRatio);
} else {
the_width = Math.round(winHeight * imageRatio);
the_height = winHeight;
}
left_limit = the_width * -2;
right_limit = the_width * 2;
image.css({
width: the_width,
height: the_height
});
$('#box').css({
width: the_width * 5,
height: the_height * 5,
top: the_height * -2,
left: the_width * -2
});
}
win.bind({
load: function() {
resizeImage();
$('.full').show('slow');
},
resize: function() {
resizeImage();
}
});