CSS reveal from corner animation - javascript

I am trying to achieve an animation effect as follows:
When a banner is shown, the bottom right corner of the next banner should be visible. When you click on this corner, it should hide the current banner and reveal the next one.
My current markup is as follows:
<div class="banners">
<div class="image active" style="background-color: red;">
<div class="corner"></div>
</div>
<div class="image" style="background-color: blue;">
<div class="corner"></div>
</div>
</div>
CSS as follows: Notice I used clip-path to create the corner:
.banners {
width: 800px;
height: 600px;
}
.image {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
.image.active {
z-index: 1;
clip-path: polygon(100% 0, 100% 65%, 60% 100%, 0 100%, 0 0);
}
.corner {
width: 50%;
height: 50%;
position: absolute;
right: 0;
bottom: 0;
}
JS:
$(document).ready(function () {
$('.corner').click(function() {
$('.image.active').removeClass('active');
$(this).parent().addClass('active');
});
});
Here is a JSFiddle of all the above code: https://jsfiddle.net/cqqxjjgu/
One immediate issue with this is that because I'm using z-index to specify that the current 'active' banner should have precedence, when remove the active class it just displays the next banner immediately, so ideally the z-index should only be changed once the animation has completed.
Does anyone have anyt idea how I can achieive this? Ideally I need a cross browser solution (not too fussed about IE < 10).

A simple example accomplishing this effect with no javascript:
https://jsfiddle.net/freer4/j2159b1e/2/
html, body{
height:100%;
width:100%;
margin:0;
padding:0;
}
.banners {
position:relative;
background:#000;
width: 100%;
height: 100%;
overflow:hidden;
}
.banners input{
display:none;
}
.slide1{
background-image: url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT5T6nwVYWsbzLcLF-JNxnGXFFFwkZMBcCMbaqeTevuldkxHg0N);
}
.slide2{
background-image:url(http://www.rd.com/wp-content/uploads/sites/2/2016/02/06-train-cat-shake-hands.jpg);
}
.slide3{
background-image:url(https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTKr6YlGNsqgJzvgBBkq1648_HsuDizVn_ZXC6iQp9kjXFzLvs1BA);
}
.image {
display:block;
height:100%;
width:100%;
position: absolute;
overflow:hidden;
z-index:1;
text-align:center;
background-position:0 0;
background-size:cover;
transition:z-index 1s step-end;
clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0);
animation-duration: 2s;
animation-name: clipout;
}
input:checked + .image{
z-index:3;
transition:z-index 1s step-end;
clip-path: polygon(100% 0, 100% 50%, 50% 100%, 0 100%, 0 0);
animation-duration: 2.2s;
animation-name: clipin;
cursor:default;
}
.image:nth-child(2),
input:checked + * + * + .image{
z-index:2;
cursor:pointer;
}
.content{
color:#FFF;
display:inline-block;
vertical-align:middle;
font-family:arial;
text-transform:uppercase;
font-size:24px;
opacity:0;
transition:0s opacity 1s;
}
input:checked + .image .content{
opacity:1;
transition:0.8s opacity 0.8s;
}
.spanner{
vertical-align:middle;
width:0;
height:100%;
display:inline-block;
}
#keyframes clipout {
from { clip-path: polygon(100% 0, 100% 50%, 50% 100%, 0 100%, 0 0); }
50% { clip-path: polygon(100% 0, 100% -100%, -100% 100%, 0 100%, 0 0); }
51% { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
to { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
}
#keyframes clipin{
from { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
50% { clip-path: polygon(100% 0, 100% 100%, 100% 100%, 0 100%, 0 0); }
to { clip-path: polygon(100% 0, 100% 50%, 50% 100%, 0 100%, 0 0); }
}
<div class="banners">
<input type="radio" id="slide1" name="slides" checked="checked" />
<label class="image slide1" for="slide1">
<div class="content">
Slide 1
</div>
<div class="spanner"></div>
</label>
<input type="radio" id="slide2" name="slides" />
<label class="image slide2" for="slide2">
<div class="content">
Slide 2
</div>
<div class="spanner"></div>
</label>
<input type="radio" id="slide3" name="slides" />
<label class="image slide3" for="slide3">
<div class="content">
Slide 3
</div>
<div class="spanner"></div>
</label>
</div>
Basically, just use keyframes to animate the clip path. Get fancy with the z-indexes and some sibling selectors.

This should work for any browser with transition support:
https://jsfiddle.net/freer4/cqqxjjgu/1/
Essentially, make a really big cover slide, with the same background color as your next slide, and pull it over your current slide. Then fade out to reveal the next slide.
So a little adjustment on the html:
<div class="banners">
<div class="image active" style="background-color: black;">
<div class="content">
Slide 1
</div>
<div class="spanner"></div>
<div class="corner" style="background-color: cyan;"></div>
</div>
<div class="image" style="background-color: cyan;">
<div class="content">
Slide 2
</div>
<div class="spanner"></div>
<div class="corner" style="background-color: magenta;"></div>
</div>
<div class="image" style="background-color: magenta;">
<div class="content">
Slide 3
</div>
<div class="spanner"></div>
<div class="corner" style="background-color: black;"></div>
</div>
</div>
Change the jQuery to select either the next slide or the first if there are no more:
$(document).ready(function () {
$('.corner').click(function() {
var $parent = $(this).parent();
$parent.removeClass("active");
if ($parent.next().length){
$parent.next().addClass("active");
} else {
$parent.prevAll().last().addClass("active");
}
});
});
And set up some intricate transitions you can adjust the timing of:
.image {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
overflow:hidden;
z-index:1;
transition:z-index 2s step-end, 1s opacity 1s ease-in-out;
text-align:center;
opacity:0;
}
.image.active{
opacity:1;
z-index:2;
transition:z-index 2s step-end, 0s opacity 0s;
}
.corner {
width: 200%;
height: 200%;
position: absolute;
top: -100%;
left: -100%;
clip-path: polygon(100% 0, 0 70%, 0 100%, 100% 100%, 100% 0, 100% 0);
z-index:3;
margin-left:150%;
margin-top:150%;
transition:2s top ease-in-out, 2s left ease-in-out, 0s margin 2s;
}
.image.active .corner{
top:0;
left:0;
margin-top:0;
margin-left:0;
transition:0s top ease-in-out 1s, 0s left ease-in-out 1s, 2s margin ease-in-out 1s;
}
Aside: This example is completely flexible (doesn't care about size):
.banners {
width: 100%;
height: 100%;
}
Or with images: https://jsfiddle.net/freer4/ens7caaL/

This is an answer without the use of clip-path, because browser compatibility on DOM elements other than svg is low.
I see now that Vadim had the same idea as me with the rotated container (hadn't checked back here until I had finished), but from what I can tell there are still enough differences between our answers to justify posting my solution:
$(document).ready(function() {
$(".slider").on("click",".next",function() {
if ($(this).prev().length) {$(this).prev().removeClass("curr");} else {$(this).siblings().last().removeClass("curr");} //deactivate current slide
if ($(this).next().length) {$(this).next().addClass("next");} else {$(this).siblings().first().addClass("next");} //prepare slide that follows next slide
$(this).removeClass("next").addClass("curr"); //activate next slide
});
});
.slider, .slider .img {
width: 55vw;
height: calc(55vw / 16*9);
background: #000 center/contain no-repeat;
}
.slider {position:relative; margin:0 auto; overflow:hidden;}
.slider .slide {
position: absolute;
z-index: 0;
width: 250%;
height: 0;
transform: translateX(-50%) rotate(-20deg);
transform-origin: 50% 0;
transition:z-index 0s 0.7s, height 0.7s;
overflow: hidden;
}
.slider .slide.next {z-index:1; height:155%; opacity:0.5; transition:z-index 0s 1.1s, height 0s 0.7s; cursor:pointer;}
.slider .slide.curr {z-index:2; height:135%; opacity:1.0; transition:z-index 0s 1.1s, height 0.4s 0.7s, opacity 0.7s;}
.slider .slide .img {margin-left:50%; transform:rotate(20deg); transform-origin:0 0;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="slider">
<div class="slide curr"><div class="img" style="background-image:url(https://placeimg.com/640/480/animals);"></div></div>
<div class="slide next"><div class="img" style="background-image:url(https://placeimg.com/640/480/people);"></div></div>
<div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/nature);"></div></div>
<div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/tech);"></div></div>
<div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/arch);"></div></div>
</div>
codepen: https://codepen.io/anon/pen/JJQRvM
jsfiddle: https://jsfiddle.net/tq2hw7b9/5/
Instead of clip-path I just change the height of the slide containers, using transition for the animation effects.
IE TROUBLES
Unfortunately, as usual, IE processes transform:rotate() differently from other browsers. Visually the rotations happen, but the browser still seems to reserve the original space of the elements, so therefor the exposed corner of the next slide is not clickable because the current slide is 'covering' it. Using the -ms- or -webkit- prefix doesn't make a difference.
The following code snippet DOES work in IE:
$(document).ready(function() {
$(".slider .corner").on("click",function() {
var $next = $(this).siblings(".next");
if ($next.prev().length) {$next.prev().removeClass("curr");} else {$next.siblings(".slide").last().removeClass("curr");} //deactivate current slide
if ($next.next(".slide").length) {$next.next().addClass("next");} else {$next.siblings().first().addClass("next");} //prepare slide that follows next slide
$next.removeClass("next").addClass("curr"); //activate next slide
});
});
.slider, .slider .img {
width: 55vw;
height: calc(55vw / 16*9);
background: #000 center/contain no-repeat;
}
.slider {position:relative; margin:0 auto; overflow:hidden;}
.slider .corner {
position: absolute;
z-index: 3;
bottom: 0;
right: 0;
width: 100%;
height: 21%;
transform: rotate(-20deg);
transform-origin: 100% 0;
cursor: pointer;
}
.slider .slide {
position: absolute;
z-index: 0;
width: 250%;
height: 0;
transform: translateX(-50%) rotate(-20deg);
transform-origin: 50% 0;
transition:z-index 0s 0.7s, height 0.7s;
overflow: hidden;
}
.slider .slide.next {z-index:1; height:155%; opacity:0.5; transition:z-index 0s 1.1s, height 0s 0.7s;}
.slider .slide.curr {z-index:2; height:135%; opacity:1.0; transition:z-index 0s 1.1s, height 0.4s 0.7s, opacity 0.7s;}
.slider .slide .img {margin-left:50%; transform:rotate(20deg); transform-origin:0 0;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="slider">
<div class="slide curr"><div class="img" style="background-image:url(https://placeimg.com/640/480/animals);"></div></div>
<div class="slide next"><div class="img" style="background-image:url(https://placeimg.com/640/480/people);"></div></div>
<div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/nature);"></div></div>
<div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/tech);"></div></div>
<div class="slide"><div class="img" style="background-image:url(https://placeimg.com/640/480/arch);"></div></div>
<div class="corner"></div>
</div>
codepen: https://codepen.io/anon/pen/GEbrzQ
jsfiddle: https://jsfiddle.net/8ggqndj1/
I added an extra <div class="corner"> that covers all the slides.
The click-handler in JS is now bound to this .corner, and at the start of the handler a reference to the next slide is stored into a variable, which is used in the rest of the code.
In CSS there is also a new rule for the .corner.
SLIDE-ARRAY IN JS
Have a look at the code snippet below, for a list of slides in JS (if someone needs it):
$(document).ready(function() {
var slides = [
2, //index for next slide
"https://placeimg.com/640/480/animals",
"https://placeimg.com/640/480/people",
"https://placeimg.com/640/480/nature",
"https://placeimg.com/640/480/tech",
"https://placeimg.com/640/480/arch"
];
//INITIALIZE SLIDESHOW--------------------
$(".slider").css("background-image","url("+slides[2]+")"); //set next slide
$(".slider .current .img").css("background-image","url("+slides[1]+")"); //set current slide, and set slide-height to slideshow-height
//SLIDESHOW CLICK-HANDLER--------------------
$(".slider .current").on("click",function(e){e.stopPropagation();});
$(".slider").on("click",function() {
$(this).children(".current").animate({height:0},700,function(){
$(this).children(".img").css("background-image","url("+slides[slides[0]]+")"); //set the current slide to the next slide
$(this).css("height","155%"); //cover entire slide
if (slides[0]==slides.length-1) {slides[0]=1;} else {++slides[0];} //increase/loop index for next slide
$(this).parent().css("background-image","url("+slides[slides[0]]+")"); //set the next slide to the next slide after that
$(this).animate({height:"135%"},400); //reveal corner for next slide
});
});
});
.slider, .slider .img {
width: 55vw;
height: calc(55vw / 16*9);
background: #000 center/contain no-repeat;
}
.slider {margin:0 auto; cursor:pointer; overflow:hidden;}
.slider .current {
width: 250%;
height: 135%;
transform: translateX(-50%) rotate(-20deg);
transform-origin: 50% 0;
overflow: hidden;
cursor: default;
}
.slider .current .img {margin-left:50%; transform:rotate(20deg); transform-origin:0 0;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="slider"><div class="current"><div class="img"></div></div></div>
codepen: https://codepen.io/anon/pen/EXBgew
jsfiddle: https://jsfiddle.net/qghv9bnh/13/
It may hold some preferences for some, although I think my first solution is a lot cleaner, faster, and flexible for adding extra slides (certainly when you use a CMS like WordPress or Joomla):
The images are only loaded when you actually use the slider, so users save bandwidth for every slide they don't click on.
The HTML is very concise and will never grow no matter how many slides you have, so your HTML will look cleaner (but if you use PHP to include them it will look just as clean, even cleaner).
Can't really think of anything else, as I said, I prefer the first one. But none the less, it may come in handy for someone.

This will work everywhere, even in IE/Edge. It's based on CSS transitions and replacing CSS classes via JavaScript.
I'm using rotated rectangle to crop images. Demo of main principle (contains a lot of hardcoded, previously calculated values):
*,
*:before,
*:after {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
div {
width: 300px;
height: 200px;
border: 1px solid red;
position: relative;
}
div:after {
content: "";
position: absolute;
left: -86.6px;
top: 50px;
width: 359.8px;
height: 240px;
transform-origin: 0 0;
transform: rotate(-30deg);
border: 1px solid blue;
}
<div></div>
Main demo (there are a lot of hardcoded values). For better understanding how it works you can add border to .slide-cropper:
$(document).ready(function() {
$(".banners").on("click", ".slide-cropper.next .slide-content", function() {
var $container = $(this).closest(".slide");
$(".slide-cropper").removeClass("prev")
.removeClass("current")
.removeClass("next");
$(this).closest(".slide-cropper").addClass("current");
var $prevContainer;
if ($container.prev().length) {
$prevContainer = $container.prev();
} else {
$prevContainer = $container.siblings(":last");
}
$prevContainer.find(".slide-cropper").addClass("prev");
var $nextContainer;
if ($container.next().length) {
$nextContainer = $container.next();
} else {
$nextContainer = $container.siblings(":first");
}
$nextContainer.find(".slide-cropper").addClass("next");
});
});
*,
*:before,
*:after {
box-sizing: border-box;
}
/* all body styles are just for demo */
/* just centering the slider */
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.banners {
width: 300px;
height: 200px;
position: relative;
}
.slide {
width: 100%;
height: 100%;
}
.slide .slide-cropper {
position: absolute;
left: -86.6px;
top: 50px;
width: 359.8px;
height: 323.2px;
transform-origin: 0 0;
transform: rotate(-30deg);
overflow: hidden;
transition: height 2s linear;
}
.slide-content {
position: absolute;
background-size: 100% 100%;
left: 100px;
top: 0;
width: 300px;
height: 200px;
transform: rotate(30deg);
transform-origin: 0 0;
z-index: 0;
/* just styles for text */
/* using flexbox to center text */
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3em;
}
.slide1 .slide-content {
background-image: url("https://i.stack.imgur.com/tt875.jpg");
}
.slide2 .slide-content {
background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
}
.slide3 .slide-content {
background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
}
.slide-cropper.prev {
height: 0;
z-index: 3;
}
.slide-cropper.current {
height: 240px;
transition-delay: 2s;
z-index: 2;
}
.slide-cropper.next {
z-index: 1;
}
/* Fix for IE */
.slide-cropper.current {
pointer-events: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="banners">
<div class="slide slide1">
<div class="slide-cropper current">
<div class="slide-content">
Slide 1
</div>
</div>
</div>
<div class="slide slide2">
<div class="slide-cropper next">
<div class="slide-content">
Slide 2
</div>
</div>
</div>
<div class="slide slide3">
<div class="slide-cropper">
<div class="slide-content">
Slide 3
</div>
</div>
</div>
</div>
To understand how it works I moved all calculations to CSS variables AKA CSS custom properties. They work in many browsers but not in combination with CSS calc function. This example works perfectly only in Chrome but will help to understand and modify this example (just replace CSS variables with calculated hardcoded values). Also you can move this calculations to CSS preprocessor or JavaScript code.
$(document).ready(function() {
$(".banners").on("click", ".slide-cropper.next .slide-content", function() {
var $container = $(this).closest(".slide");
$(".slide-cropper").removeClass("prev")
.removeClass("current")
.removeClass("next");
$(this).closest(".slide-cropper").addClass("current");
var $prevContainer;
if ($container.prev().length) {
$prevContainer = $container.prev();
} else {
$prevContainer = $container.siblings(":last");
}
$prevContainer.find(".slide-cropper").addClass("prev");
var $nextContainer;
if ($container.next().length) {
$nextContainer = $container.next();
} else {
$nextContainer = $container.siblings(":first");
}
$nextContainer.find(".slide-cropper").addClass("next");
});
});
*,
*:before,
*:after {
box-sizing: border-box;
}
html {
--width: 300px;
--height: 200px;
/* rotate for image cropping */
--rotate-angle: 30deg;
/* sin 30 degrees for image cropping */
--sin-rotate-angle: 0.5;
/* cos 30 degrees for image cropping */
--cos-rotate-angle: 0.8660254037844386;
/* clipper ratio for width, can be from 0 to 1 */
--clipper-ratio: 0.45;
--animation-timeout: 2s;
}
/* all body styles are just for demo */
/* just centering the slider */
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.banners {
width: var(--width);
height: var(--height);
position: relative;
}
.slide {
width: 100%;
height: 100%;
}
.slide .slide-cropper {
position: absolute;
left: calc(-1 * var(--height) * var(--sin-rotate-angle) * var(--cos-rotate-angle));
top: calc(var(--height) * var(--sin-rotate-angle) * var(--sin-rotate-angle));
width: calc(var(--height) * var(--sin-rotate-angle) + var(--width) * var(--cos-rotate-angle));
height: calc(var(--height) * var(--cos-rotate-angle) + var(--width) * var(--sin-rotate-angle));
transform-origin: 0 0;
transform: rotate(calc(-1 * var(--rotate-angle)));
overflow: hidden;
transition: height var(--animation-timeout) linear;
}
.slide-content {
position: absolute;
background-size: 100% 100%;
left: calc(var(--height) / 2);
width: var(--width);
height: var(--height);
transform: rotate(var(--rotate-angle));
transform-origin: 0 0;
z-index: 0;
/* just styles for text */
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3em;
}
.slide1 .slide-content {
background-image: url("https://i.stack.imgur.com/tt875.jpg");
}
.slide2 .slide-content {
background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
}
.slide3 .slide-content {
background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
}
.slide-cropper.prev {
height: 0;
z-index: 3;
}
.slide-cropper.current {
height: calc(var(--height) * var(--cos-rotate-angle) + var(--clipper-ratio) * var(--width) * var(--sin-rotate-angle));
transition-delay: var(--animation-timeout);
z-index: 2;
}
.slide-cropper.next {
z-index: 1;
}
/* Fix for IE */
.slide-cropper.current {
pointer-events: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="banners">
<div class="slide slide1">
<div class="slide-cropper current">
<div class="slide-content">
Slide 1
</div>
</div>
</div>
<div class="slide slide2">
<div class="slide-cropper next">
<div class="slide-content">
Slide 2
</div>
</div>
</div>
<div class="slide slide3">
<div class="slide-cropper">
<div class="slide-content">
Slide 3
</div>
</div>
</div>
</div>
To change this to fullscreen you need to set --width: 100vw and --height: 100vh. (Of course, then you'll have to replace CSS variables with hardcoded values to work in all browsers). Demo:
$(document).ready(function() {
$(".banners").on("click", ".slide-cropper.next .slide-content", function() {
var $container = $(this).closest(".slide");
$(".slide-cropper").removeClass("prev")
.removeClass("current")
.removeClass("next");
$(this).closest(".slide-cropper").addClass("current");
var $prevContainer;
if ($container.prev().length) {
$prevContainer = $container.prev();
} else {
$prevContainer = $container.siblings(":last");
}
$prevContainer.find(".slide-cropper").addClass("prev");
var $nextContainer;
if ($container.next().length) {
$nextContainer = $container.next();
} else {
$nextContainer = $container.siblings(":first");
}
$nextContainer.find(".slide-cropper").addClass("next");
});
});
*,
*:before,
*:after {
box-sizing: border-box;
}
html {
--width: 100vw;
--height: 100vh;
/* rotate for image cropping */
--rotate-angle: 30deg;
/* sin 30 degrees for image cropping */
--sin-rotate-angle: 0.5;
/* cos 30 degrees for image cropping */
--cos-rotate-angle: 0.8660254037844386;
/* clipper ratio for width, can be from 0 to 1 */
--clipper-ratio: 0.45;
--animation-timeout: 2s;
}
/* all body styles are just for demo */
/* just centering the slider */
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.banners {
width: var(--width);
height: var(--height);
position: relative;
}
.slide {
width: 100%;
height: 100%;
}
.slide .slide-cropper {
position: absolute;
left: calc(-1 * var(--height) * var(--sin-rotate-angle) * var(--cos-rotate-angle));
top: calc(var(--height) * var(--sin-rotate-angle) * var(--sin-rotate-angle));
width: calc(var(--height) * var(--sin-rotate-angle) + var(--width) * var(--cos-rotate-angle));
height: calc(var(--height) * var(--cos-rotate-angle) + var(--width) * var(--sin-rotate-angle));
transform-origin: 0 0;
transform: rotate(calc(-1 * var(--rotate-angle)));
overflow: hidden;
transition: height var(--animation-timeout) linear;
}
.slide-content {
position: absolute;
background-size: 100% 100%;
left: calc(var(--height) / 2);
width: var(--width);
height: var(--height);
transform: rotate(var(--rotate-angle));
transform-origin: 0 0;
z-index: 0;
/* just styles for text */
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3em;
}
.slide1 .slide-content {
background-image: url("https://i.stack.imgur.com/tt875.jpg");
}
.slide2 .slide-content {
background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
}
.slide3 .slide-content {
background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
}
.slide-cropper.prev {
height: 0;
z-index: 3;
}
.slide-cropper.current {
height: calc(var(--height) * var(--cos-rotate-angle) + var(--clipper-ratio) * var(--width) * var(--sin-rotate-angle));
transition-delay: var(--animation-timeout);
z-index: 2;
}
.slide-cropper.next {
z-index: 1;
}
/* Fix for IE */
.slide-cropper.current {
pointer-events: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="banners">
<div class="slide slide1">
<div class="slide-cropper current">
<div class="slide-content">
Slide 1
</div>
</div>
</div>
<div class="slide slide2">
<div class="slide-cropper next">
<div class="slide-content">
Slide 2
</div>
</div>
</div>
<div class="slide slide3">
<div class="slide-cropper">
<div class="slide-content">
Slide 3
</div>
</div>
</div>
</div>
Also demo with CSS variables that work in Firefox (Firefox is not friendly with combination of CSS variables and transform: rotate, so I just replaced transform: rotate with hardcoded values):
$(document).ready(function() {
$(".banners").on("click", ".slide-cropper.next .slide-content", function() {
var $container = $(this).closest(".slide");
$(".slide-cropper").removeClass("prev")
.removeClass("current")
.removeClass("next");
$(this).closest(".slide-cropper").addClass("current");
var $prevContainer;
if ($container.prev().length) {
$prevContainer = $container.prev();
} else {
$prevContainer = $container.siblings(":last");
}
$prevContainer.find(".slide-cropper").addClass("prev");
var $nextContainer;
if ($container.next().length) {
$nextContainer = $container.next();
} else {
$nextContainer = $container.siblings(":first");
}
$nextContainer.find(".slide-cropper").addClass("next");
});
});
*,
*:before,
*:after {
box-sizing: border-box;
}
html {
--width: 100vw;
--height: 100vh;
/* sin 30 degrees for image cropping */
--sin-rotate-angle: 0.5;
/* cos 30 degrees for image cropping */
--cos-rotate-angle: 0.8660254037844386;
/* clipper ratio for width, can be from 0 to 1 */
--clipper-ratio: 0.45;
--animation-timeout: 2s;
}
/* all body styles are just for demo */
/* just centering the slider */
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.banners {
width: var(--width);
height: var(--height);
position: relative;
}
.slide {
width: 100%;
height: 100%;
}
.slide .slide-cropper {
position: absolute;
left: calc(-1 * var(--height) * var(--sin-rotate-angle) * var(--cos-rotate-angle));
top: calc(var(--height) * var(--sin-rotate-angle) * var(--sin-rotate-angle));
width: calc(var(--height) * var(--sin-rotate-angle) + var(--width) * var(--cos-rotate-angle));
height: calc(var(--height) * var(--cos-rotate-angle) + var(--width) * var(--sin-rotate-angle));
transform-origin: 0 0;
transform: rotate(-30deg);
overflow: hidden;
transition: height var(--animation-timeout) linear;
}
.slide-content {
position: absolute;
background-size: 100% 100%;
left: calc(var(--height) / 2);
width: var(--width);
height: var(--height);
transform: rotate(30deg);
transform-origin: 0 0;
z-index: 0;
/* just styles for text */
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 3em;
}
.slide1 .slide-content {
background-image: url("https://i.stack.imgur.com/tt875.jpg");
}
.slide2 .slide-content {
background-image: url("https://i.stack.imgur.com/hzbmw.jpg");
}
.slide3 .slide-content {
background-image: url("https://i.stack.imgur.com/4UxLW.jpg");
}
.slide-cropper.prev {
height: 0;
z-index: 3;
}
.slide-cropper.current {
height: calc(var(--height) * var(--cos-rotate-angle) + var(--clipper-ratio) * var(--width) * var(--sin-rotate-angle));
transition-delay: var(--animation-timeout);
z-index: 2;
}
.slide-cropper.next {
z-index: 1;
}
/* Fix for IE */
.slide-cropper.current {
pointer-events: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="banners">
<div class="slide slide1">
<div class="slide-cropper current">
<div class="slide-content">
Slide 1
</div>
</div>
</div>
<div class="slide slide2">
<div class="slide-cropper next">
<div class="slide-content">
Slide 2
</div>
</div>
</div>
<div class="slide slide3">
<div class="slide-cropper">
<div class="slide-content">
Slide 3
</div>
</div>
</div>
</div>

This sample working on Firefox, Chrome, IE.
For change sliding rule change transition
$(document).ready(function () {
$('.angle').click(function() {
var $parent = $(this).parent();
$parent.removeClass("current");
if ($parent.next().length){
$parent.next().addClass("current");
} else {
$parent.prevAll().last().addClass("current");
}
});
});
body{
height:100%;
width:100%;
}
.slideShow {
width: 100%;
height: 100%;
}
.image {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
overflow:hidden;
z-index:1;
transition:z-index 2s step-end, 1s opacity 1s ease-in-out;
text-align:center;
opacity:0;
background-size:100% 100%;
background-attachment:fixed;
}
.image.current{
opacity:1;
z-index:2;
transition:z-index 2s step-end, 0s opacity 0s;
}
.angle {
width: 200%;
height: 200%;
position: absolute;
top: -100%;
left: -100%;
clip-path: polygon(100% 0, 0 70%, 0 100%, 100% 100%, 100% 0, 100% 0);
z-index:3;
margin-left:150%;
margin-top:150%;
transition:2s top ease-in-out, 2s left ease-in-out, 0s margin 2s;
background-size:100% 100%;
background-attachment:fixed;
}
.image.current .angle{
top:0;
left:0;
margin-top:0;
margin-left:0;
transition:0s top ease-in-out 1s, 0s left ease-in-out 1s, 2s margin ease-in-out 1s;
}
.main{
color:#FFF;
display:inline-block;
vertical-align:middle;
font-family:arial;
text-transform:uppercase;
font-size:24px;
}
.middle{
vertical-align:middle;
width:0;
height:100%;
display:inline-block;
}
.image1, .image3 .angle{
background-image: url(http://i3.imgbus.com/doimg/4c5o0m8m6o5n4e0.png);
}
.image1 .angle, .image2{
background-image:url(http://i4.imgbus.com/doimg/1c7obm6m1o3nbb0.png);
}
.image2 .angle, .image3{
background-image:url(http://i3.imgbus.com/doimg/ccbo5m2m8o8n759.jpg);
}
<div class="slideShow">
<div class="image image1 current">
<div class="main">
</div>
<div class="middle"></div>
<div class="angle" style="background-color: cyan;"></div>
</div>
<div class="image image2" style="background-color: cyan;">
<div class="main">
</div>
<div class="middle"></div>
<div class="angle" style="background-color: magenta;"></div>
</div>
<div class="image image3" style="background-color: magenta;">
<div class="main">
</div>
<div class="middle"></div>
<div class="angle"></div>
</div>
</div>
Also you can use SildeShow Maker softwares like Amazing Slider.
easy download, easy use: download link
it's very powerful software. see Screenshot

Related

Progress bar different colors

how would you make progress bar in CSS that would have colours based on values etc. from 0% to 20% red colour, 20% to 40% blue... Also, I would want to show the colours all the time, not only when it hits the value(so that part of a progress bar would be red, part blue and the other colours from the beggining and that the colours would disappear as the value would go down).
If you are trying to achieve a gradient progress bar as per the current progress, then try linear-gradient() property in CSS.
Here is a working model:
#prog-bar-cont {
width: 75vw;
height: 2.5em;
}
#prog-bar-cont #prog-bar {
background: #ffff;
width: 100%;
height: 100%;
border-color: #000;
border-style: solid;
border-radius: 50px;
overflow: hidden;
position: relative;
}
#prog-bar-cont #prog-bar #background {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
/*Actual Stuff*/
background: linear-gradient(-90deg, violet, #30b3fc, #70dc23, yellow, orange, #ff1076);
-webkit-clip-path: inset(0 100% 0 0);
clip-path: inset(0 100% 0 0);
transition: all 3s;
-webkit-transition: all 3s;
}
#prog-bar-cont:hover #prog-bar #background {
-webkit-clip-path: inset(0 0 0 0);
clip-path: inset(0 0 0 0);
}
<h1>Rainbow Progress Bar</h1>
<p>Try hovering over the bar</p>
<div id='prog-bar-cont'>
<div id="prog-bar">
<div id="background"></div>
</div>
</div>
You can accomplish that by nesting the progress bar in a parent element and applying the css property overflow: hidden.
You can change the width of the class bar-clipper to the desired percentage. i.e. calc(300px * 0.6) will show 60% of the bar.
.bar-clipper {
width: calc(300px * 0.8);
height: 20px;
overflow: hidden;
position: absolute;
}
.bar-wrapper {
width: 300px;
height: 20px;
display: flex;
position: absolute;
}
.bar-wrapper span {
width: 100%;
height: 100%;
}
.bar-wrapper .bar1 {
background-color: #163f5f;
}
.bar-wrapper .bar2 {
background-color: #21639b;
}
.bar-wrapper .bar3 {
background-color: #3caea3;
}
.bar-wrapper .bar4 {
background-color: #f6d65b;
}
.bar-wrapper .bar5 {
background-color: #ed543b;
}
<body>
<div class="bar-clipper">
<div class="bar-wrapper">
<span class="bar1"></span>
<span class="bar2"></span>
<span class="bar3"></span>
<span class="bar4"></span>
<span class="bar5"></span>
</div>
</div>
</body>
Link to fiddle: https://jsfiddle.net/L13yrgbm/

How to make a child div override its parent position in a grid?

I'm currently building my own graphic design portfolio, which is grid based (inspired by this concept https://codepen.io/jkantner/pen/KQPdXK (there is my codepen at the end of the post with my modified version)) and I'm struggling to get some features to work.
I have a series of div, one per project I want to showcase, which are referred by the class .stack:nth-child(n) individually and by the classes .card and .top as a whole. Those cards are contained in the .cards-div with display: grid property applied. A transform property is also applied to it to create the 3D-perspective effect.
I want to make a card, that, once active/focused, stands in front of everything and to show in details its particular project. I already succeeded in making it bigger than the others, getting it in another position and reversing the 3D-effect of its parent but I cannot figure out how to make all the cards go to the same place once focused, because they retain their position induced by the parent.
How should I proceed to set one position for all focus/active cards and overriding their position in their parent's grid ?
Here's my messy code although you probably should read it on CodePen as you'll probably see the mobile version here because of the size of the snippet thumbnail: https://codepen.io/Goo_m_Ba/pen/gdqGrP
window.addEventListener("scroll", scrollGrid);
window.addEventListener("wheel", noShakeScroll);
function scrollGrid() {
let transY = window.pageYOffset,
cards = document.querySelector(".cards");
cards.style.setProperty("--scroll", transY + "px");
}
scrollGrid();
/* Without this, the `items` div erratically shakes while scrolling with wheel or touchpad. The issue still persists in Safari though… */
function noShakeScroll(e) {
this.scrollBy(0, e.deltaY);
e.preventDefault();
}
var $stickyBlock = document.querySelector('.portfolio');
var origOffsetY = $stickyBlock.offsetTop - 15; // 15 is your top margin
function onScroll() {
window.scrollY >= origOffsetY ? $stickyBlock.classList.add('sticky') :
$stickyBlock.classList.remove('sticky');
}
document.addEventListener('scroll', onScroll);
*,
*:before,
*:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
font-size: 1vw;
--cardW: 20vw;
--cardH: 20vw;
--cardZInc: 0.5em;
--gap: 1.5em;
--ttxt: 0.5em;
}
a {
text-decoration: none;
color: white;
}
body {
background-image: repeating-linear-gradient(35deg, #eeeeee, #dddddd, transparent 2px, transparent 80px), repeating-linear-gradient(-35deg, #eeeeee, #dddddd, transparent 2px, transparent 80px);
background-attachment: fixed;
font: 1em "Open Sans", sans-serif;
height: 100vh;
overflow-x: hidden;
position: sticky;
}
/* Grid */
.cards,
.stack {
transform-style: preserve-3d;
}
.portfolio {
text-align: center;
font: 1em "Rubik Mono One", sans-serif;
/* letter-spacing: 0.5em; */
font-size: 1.1em;
text-color: white;
grid-template: repeat(12, calc (var(--cardH)*2)) / var(--cardW);
z-index: 0;
outline: 50px solid white;
position: absolute;
background-color: white;
width: 100%;
height: 40em;
transform: translateZ(-10px);
top: calc(-1% - var(--gap)*2);
opacity: 1;
}
.stack .contents {
color: #faf;
position: relative;
}
.cards {
--scroll: 100px;
display: grid;
grid-template: repeat(12 var(--cardH)) / var(--cardW);
grid-gap: var(--gap);
padding-bottom: calc(var(--cardH) * 3);
position: absolute;
transform: translate(-20%, calc(-50% + var(--scroll))) rotateX(45deg) rotateZ(45deg) translateY(calc(50% - var(--scroll)));
z-index: 1;
}
/* Card hover */
.stack:hover .top,
.stack:focus .top {
transition: all 0.5s;
transform: translateZ(calc(var(--cardZInc)*4))rotateX(-20deg);
transform-origin: center bottom;
z-index: 2
}
.stack:nth-child(2):active .top {
transform: translateX(calc(0px - calc(var(--gap) * 1) + calc(var(--cardW) * 1))) translateY(calc(0px + calc(var(--gap) * 1) + calc(var(--cardW) * 1))) translateZ(16vw) rotateX(-25.1deg) rotateY(27deg) rotateZ(-40deg);
}
.stack:nth-child(3):active .top {
transform: translateX(calc(0px + calc(var(--gap) * 1) - calc(var(--cardW) * 0.5))) translateY(calc(0px + calc(var(--gap) * 4) + calc(var(--cardW) * 1))) translateZ(16vw) rotateX(-25.1deg) rotateY(27deg) rotateZ(-40deg);
}
.stack:nth-child(4):focus .top {
transform: translateX(calc(0px + calc(var(--gap) * 0) - calc(var(--cardW) * 1.5))) translateY(calc(0px + calc(var(--gap) * 1) + calc(var(--cardW) * 1))) translateZ(16vw)rotateX(-25.1deg) rotateY(27deg) rotateZ(-40deg);
}
.stack:focus .top {
transform-origin: center bottom;
transform: translateZ(16vw) rotateX(-25.1deg) rotateY(27deg) rotateZ(-40deg);
height: 70vh;
width: 70vw;
/*translate(-20%, calc(-50% + var(--scroll))) rotateX(45deg)
rotateZ(45deg) translateY(calc(50% - var(--scroll)));*/
z-index: 15;
/*width:50vw ;
height:50vw;*/
}
.stack:hover .shadow,
.stack:focus .shadow {
filter: blur(5px);
-webkit-filter: blur(5px);
opacity: 0.2;
transform-origin: center bottom;
height: 80%;
transform: translatey(20%);
}
/* Other card styles */
.card {
/*background-image: url(https://cdn.weasyl.com/static/media/dd/c2/45/ddc2458130dd90cf4d5f5783f14f7835cf980110df9131354325a358a3d3d60d.png);*/
background-color: black;
background-size: 200% 200%;
box-shadow: -1px -1px 0 rgba(0, 0, 0, 0.2) inset;
color: #000;
padding: 0.75em;
position: absolute;
transition: all 0.3s;
width: 100%;
height: 100%;
outline: 5px solid green;
}
.top {
transform: translateZ(10px);
z-index: 15;
}
.shadow {
background: #000;
filter: blur(2px);
-webkit-filter: blur(2px);
opacity: 0.2;
}
/* Alter grid at breakpoints */
#media screen and (max-width: 550px) {
:root {
font-size: 5vw;
--cardW: 100vw;
--cardH: 100vw;
--cardZInc: 10.5em;
--ttxt: 0.5em;
}
.stack:hover .top,
.stack:focus .top {
transform: translateZ(calc(var(--cardZInc)*0))rotateX(0deg);
transform-origin: center bottom;
}
.cards {
grid-template: repeat(24, var(--cardH)) / repeat(1, var(--cardW));
transform: translate(-0, calc(0 + var(--scroll))) rotateX(0deg) rotateZ(0deg) translateY(calc(0% - var(--scroll)));
left: 0%;
top: 25%;
}
.titre {
transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg) translateZ(0px);
bottom: 80vh;
left: 10px;
white-space: nowrap;
}
.portfolio {
font-size: 6vw;
letter-spacing: 0em;
opacity: 0;
width: 100vw;
top: -100px;
}
#dg {
bottom: 5vh
}
}
#media screen and (min-width: 551px) {
#nom {
white-space: nowrap;
width: 100%;
}
#dg {
margin-top: 10vh;
}
.cards {
grid-template: repeat(16, var(--cardH)) / repeat(2, var(--cardW));
top: 50%;
left: 60%;
}
:root {
font-size: 2vw;
--cardW: 40vw;
--cardH: 40vw;
--cardZInc: 0.5em;
--ttxt: 1em;
}
}
#media screen and (min-width: 1050px) {
#nom {
white-space: normal;
}
#dg {
font-size: calc(var(--ttxt) * 0.7);
margin-top: 13vw;
}
.cards {
grid-template: repeat(12, var(--cardH)) / repeat(3, var(--cardW));
top: 35%;
left: 60%
}
:root {
font-size: 2vw;
--cardW: 18vw;
--cardH: 18vw;
--cardZInc: 0.5em;
--ttxt: 1em;
}
}
<div class="cards">
<div class="portfolio">
<div class="contents">
<p>nice portfolio</p>
</div>
</div>
<a class="stack" href="#">
<div class="card top">
<div class="contents">
<p id="projets">1 NOM DU PROJET </p>
</div>
</div>
<div class="card shadow"></div>
</a>
<a class="stack" href="#">
<div class="card top">
<div class="contents">
<p id="projets">NOM DU PROJET </p>
</div>
</div>
<div class="card shadow"></div>
</a>
<a class="stack" href="#">
<div class="card top">
<div class="contents">
<p id="projets">NOM DU PROJET </p>
</div>
</div>
<div class="card shadow"></div>
</a>
<a class="stack" href="#">
<div class="card top">
<div class="contents">
<p id="projets">NOM DU PROJET </p>
</div>
</div>
<div class="card shadow"></div>
</a>
<a class="stack" href="#">
<div class="card top">
<div class="contents">
<p id="projets">NOM DU PROJET </p>
</div>
</div>
<div class="card shadow"></div>
</a>
<a class="stack" href="#">
<div class="card top">
<div class="contents">
<p id="projets">NOM DU PROJET </p>
</div>
</div>
<div class="card shadow"></div>
</a>
A html element is relative to it's first positioned parent. To cancel positioning on an element, you can set the rule
position: static;

Transition after adding a class with jQuery isn't working as expected

I got two columns of images, done with column-count:2. When I press one of the images, jQuery duplicates that image at the exact same position with position:absolute. Then a class .dupAnim gets added, which makes that image full width and transition it to top:0;left:0 of the page.
So my problem:When I click an image, the transition from the original state to top:0; left:0; width:100% isn't working. But when I click the close button, it's perfectly transitioning back.
It seems like the in transition isn't working, but the out transition is working. Below you can find a snippet. Does anyone know what the problem is?
$(".item").each(function(){
var imageSrc = $(this).children('img').attr('src');
$(this).css('background-image', 'url(' + imageSrc + ')');
$(this).find('.clipped').css('background-image', 'url(' + imageSrc + ')');
var imgHeight = $(this).find('img').height();
$(this).css('height', imgHeight + 'px');
});
$(".item").click(function(){
$(this).clone().appendTo(".duplicated").addClass("dupe");
var width = $(this).width();
var height = $(this).height();
var top = $(this).offset().top;
var left = $(this).offset().left;
var dupe = ".dupe";
$(dupe).css({
'width': width + 'px',
'height': height + 'px',
'top': top + 'px',
'left': left + 'px',
'position': 'fixed'
});
$(dupe).addClass("dupAnim");
$(".portfolio-close").fadeIn();
});
$(".portfolio-close").click(function(){
$(".duplicated").find(".dupAnim").removeClass("dupAnim");
setTimeout(function(){
$(".duplicated").children().remove();
}, 500);
$(this).fadeOut();
});
* {
font-family: 'Source Sans Pro', sans-serif;
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.wrapper {
padding: 5% 10%;
}
.wrapper .page-title {
font-size: 4em;
font-weight: 900;
margin-bottom: 20px;
}
.wrapper #portfolio-items {
-webkit-column-count: 2;
-webkit-column-gap: 0px;
-moz-column-count: 2;
-moz-column-gap: 0px;
column-count: 2;
column-gap: 10px;
}
.wrapper #portfolio-items .item {
-webkit-column-break-inside: avoid;
-webkit-backface-visibility: hidden;
border-radius: 8px;
overflow: hidden;
margin: 0 0 10px 0;
cursor: pointer;
width: 100%;
background: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .item .clipped {
padding: 0 30px 10px 20px;
display: flex;
align-items: flex-end;
height: 100%;
line-height: 1.2;
overflow: hidden;
color: #fff;
font-size: 1.7em;
background: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
-webkit-filter: invert(100%) hue-rotate(0deg);
/* change hue-rotate to play with tint */
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .item .clipped .item-title {
overflow: hidden;
}
.wrapper #portfolio-items .item img {
width: 100%;
visibility: hidden;
}
.wrapper #portfolio-items .duplicated .dupe {
position: fixed;
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .duplicated .dupe.dupAnim {
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100px !important;
border-radius: 0 !important;
box-shadow: none !important;
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .portfolio-close {
position: fixed;
z-index: 21;
cursor: pointer;
top: 0;
right: 0;
background: #515151;
width: 50px;
height: 50px;
font-size: 30px;
color: white;
padding: 10px 0 0 13px;
line-height: 1;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div id="portfolio-items">
<div class="item">
<div class="clipped">
<h1 class="item-title">Yoga</h1>
</div>
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Elephant</h1>
</div>
<img src="https://s3-us-west-1.amazonaws.com/powr/defaults/image-slider2.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Bird</h1>
</div>
<img src="http://www.gettyimages.ca/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">View</h1>
</div>
<img src="https://upload.wikimedia.org/wikipedia/commons/6/64/Ailurus_fulgens_RoterPanda_LesserPanda-2.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Dog</h1>
</div>
<img src="https://www.codeproject.com/KB/GDI-plus/ImageProcessing2/flip.jpg">
</div>
<div class="duplicated"></div>
<div class="portfolio-close">✕</div>
</div>
</div>
I think you want to add the fadeIn and the addition of the dupe class as callbacks to the container being faded in. This ensures that the callback it not executed until after the first effect is complete.
Also, I've added: $(".portfolio-close").hide(); at the beginning of the code to ensure that the container always starts off hidden because after clicking one item, it will be visible and you need it hidden for each click so that when it's class changes, the transition will work.
I reorganized the creation of the duplicate a little (to remove unnecessary variables and clean up the code a bit) and I added the all parameter to your transition properties:
$(".item").each(function(){
var imageSrc = $(this).children('img').attr('src');
$(this).css('background-image', 'url(' + imageSrc + ')');
$(this).find('.clipped').css('background-image', 'url(' + imageSrc + ')');
var imgHeight = $(this).find('img').height();
$(this).css('height', imgHeight + 'px');
});
$(".item").click(function(){
var $clone = $(this).clone();
// Make sure the container is hidden before the effects begin:
$(".portfolio-close").hide();
$clone.appendTo(".duplicated");
$clone.hide();
// Passing a function as the second argument to JQuery's show(),
// hide(), fadeIn(), fadeOut(), etc. ensures that the function
// is run AFTER the first effect is complete.
$(".portfolio-close").fadeIn(50, function(){
$clone.fadeIn();
$clone.addClass("dupe");
});
// No need to create varaibles for values you are only
// going to access just once. Just get the values directly:
$clone.css({
'width': $(this).width() + 'px',
'height': $(this).height() + 'px',
'top': $(this).offset().top + 'px',
'left': $(this).offset().left + 'px',
'position': 'fixed'
});
$clone.addClass("dupAnim");
});
$(".portfolio-close").click(function(){
$(".duplicated").find(".dupAnim").removeClass("dupAnim");
setTimeout(function(){
$(".duplicated").children().remove();
}, 500);
$(this).fadeOut();
});
* {
font-family: 'Source Sans Pro', sans-serif;
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.wrapper {
padding: 5% 10%;
}
.wrapper .page-title {
font-size: 4em;
font-weight: 900;
margin-bottom: 20px;
}
.wrapper #portfolio-items {
-webkit-column-count: 2;
-webkit-column-gap: 0px;
-moz-column-count: 2;
-moz-column-gap: 0px;
column-count: 2;
column-gap: 10px;
}
.wrapper #portfolio-items .item {
-webkit-column-break-inside: avoid;
-webkit-backface-visibility: hidden;
border-radius: 8px;
overflow: hidden;
margin: 0 0 10px 0;
cursor: pointer;
width: 100%;
background: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
transition:all .5s ease-in-out;
}
.wrapper #portfolio-items .item .clipped {
padding: 0 30px 10px 20px;
display: flex;
align-items: flex-end;
height: 100%;
line-height: 1.2;
overflow: hidden;
color: #fff;
font-size: 1.7em;
background: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
-webkit-filter: invert(100%) hue-rotate(0deg);
/* change hue-rotate to play with tint */
transition:all .5s ease-in-out;
}
.wrapper #portfolio-items .item .clipped .item-title {
overflow: hidden;
}
.wrapper #portfolio-items .item img {
width: 100%;
visibility: hidden;
}
.wrapper #portfolio-items .duplicated .dupe {
position: fixed;
transition:all .5s ease-in-out;
}
.wrapper #portfolio-items .duplicated .dupe.dupAnim {
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100px !important;
border-radius: 0 !important;
box-shadow: none !important;
transition:all .5s ease-in-out;
}
.wrapper #portfolio-items .portfolio-close {
position: fixed;
z-index: 21;
cursor: pointer;
top: 0;
right: 0;
background: #515151;
width: 50px;
height: 50px;
font-size: 30px;
color: white;
padding: 10px 0 0 13px;
line-height: 1;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div id="portfolio-items">
<div class="item">
<div class="clipped">
<h1 class="item-title">Yoga</h1>
</div>
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Elephant</h1>
</div>
<img src="https://s3-us-west-1.amazonaws.com/powr/defaults/image-slider2.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Bird</h1>
</div>
<img src="http://www.gettyimages.ca/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">View</h1>
</div>
<img src="https://upload.wikimedia.org/wikipedia/commons/6/64/Ailurus_fulgens_RoterPanda_LesserPanda-2.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Dog</h1>
</div>
<img src="https://www.codeproject.com/KB/GDI-plus/ImageProcessing2/flip.jpg">
</div>
<div class="duplicated"></div>
<div class="portfolio-close">✕</div>
</div>
</div>
The transitions are working fine, it's just that your duplicates are not starting from the same shape/size as the original.
Update you jQuery and add a SetTimeout() when adding the dupAnim class, then wrap() the duplicate img with a new container.
$(".item").click(function(){
// changed the container
$(this).clone().appendTo(".duplicated").wrap('<span class="img-container dupe">');
var width = $(this).width();
var height = $(this).height();
var top = $(this).offset().top;
var left = $(this).offset().left;
var dupe = ".dupe";
$(dupe).css({
'width': width + 'px',
'height': height + 'px',
'top': top + 'px',
'left': left + 'px',
'position': 'fixed',
});
// settime out
setTimeout(function(){
$(dupe).addClass("dupAnim");
},100) ;
$(".portfolio-close").fadeIn();
});
$(".portfolio-close").click(function(){
$(".duplicated").find(".dupAnim").removeClass("dupAnim");
setTimeout(function(){
$(".duplicated").children().remove();
}, 500);
$(this).fadeOut();
});
Your transition: .5s ease-in-out for .dupe will invoke the transition when you modify the initial style (eg, modify the width: 100% of the image to width + 'px' which computed through js) with the next code:
```
$(dupe).css({
'width': width + 'px',
'height': height + 'px',
'top': top + 'px',
'left': left + 'px',
'position': 'fixed',
})
```
Then if you immediately addClass('dupAnim'), it will override the former style modify(eg, set width to 100% !important) so, you will not see the transition of the width.
So you can add a extra className for unset the transition before set the style computed through js, and then remove the className.
Add the more thing is that you need add a setTimeout when modify the className to invoke the reflow of the browser.
$(".item").each(function(){
var imageSrc = $(this).children('img').attr('src');
$(this).css('background-image', 'url(' + imageSrc + ')');
$(this).find('.clipped').css('background-image', 'url(' + imageSrc + ')');
var imgHeight = $(this).find('img').height();
$(this).css('height', imgHeight + 'px');
});
$(".item").click(function(){
$(this).clone().appendTo(".duplicated").addClass("dupe");
var width = $(this).width();
var height = $(this).height();
var top = $(this).offset().top;
var left = $(this).offset().left;
var dupe = ".dupe";
$(dupe).addClass('no-transition').css({
'width': width + 'px',
'height': height + 'px',
'top': top + 'px',
'left': left + 'px',
'position': 'fixed',
});
setTimeout(function() {
$(dupe).removeClass('no-transition');
}, 10);
setTimeout(function() {
$(dupe).addClass("dupAnim")
$(".portfolio-close").fadeIn(500);
}, 30);
});
$(".portfolio-close").click(function(){
$(".duplicated").find(".dupAnim").removeClass("dupAnim");
setTimeout(function(){
$(".duplicated").children().remove();
}, 500);
$(this).fadeOut();
});
* {
font-family: 'Source Sans Pro', sans-serif;
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.wrapper {
padding: 5% 10%;
}
.wrapper .page-title {
font-size: 4em;
font-weight: 900;
margin-bottom: 20px;
}
.wrapper #portfolio-items {
-webkit-column-count: 2;
-webkit-column-gap: 0px;
-moz-column-count: 2;
-moz-column-gap: 0px;
column-count: 2;
column-gap: 10px;
}
.wrapper #portfolio-items .item {
-webkit-column-break-inside: avoid;
-webkit-backface-visibility: hidden;
border-radius: 8px;
overflow: hidden;
margin: 0 0 10px 0;
cursor: pointer;
width: 100%;
background: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .item .clipped {
padding: 0 30px 10px 20px;
display: flex;
align-items: flex-end;
height: 100%;
line-height: 1.2;
overflow: hidden;
color: #fff;
font-size: 1.7em;
background: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: 50% 50%;
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
-webkit-filter: invert(100%) hue-rotate(0deg);
/* change hue-rotate to play with tint */
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .item .clipped .item-title {
overflow: hidden;
}
.wrapper #portfolio-items .item img {
width: 100%;
visibility: hidden;
}
.wrapper #portfolio-items .duplicated .dupe {
position: fixed;
}
.no-transition {
transition: unset !important;
}
.wrapper #portfolio-items .duplicated .dupe.dupAnim {
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100px !important;
border-radius: 0 !important;
box-shadow: none !important;
transition: .5s ease-in-out;
}
.wrapper #portfolio-items .portfolio-close {
position: fixed;
z-index: 21;
cursor: pointer;
top: 0;
right: 0;
background: #515151;
width: 50px;
height: 50px;
font-size: 30px;
color: white;
padding: 10px 0 0 13px;
line-height: 1;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div id="portfolio-items">
<div class="item">
<div class="clipped">
<h1 class="item-title">Yoga</h1>
</div>
<img src="https://www.w3schools.com/howto/img_fjords.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Elephant</h1>
</div>
<img src="https://s3-us-west-1.amazonaws.com/powr/defaults/image-slider2.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Bird</h1>
</div>
<img src="http://www.gettyimages.ca/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">View</h1>
</div>
<img src="https://upload.wikimedia.org/wikipedia/commons/6/64/Ailurus_fulgens_RoterPanda_LesserPanda-2.jpg">
</div>
<div class="item">
<div class="clipped">
<h1 class="item-title">Dog</h1>
</div>
<img src="https://www.codeproject.com/KB/GDI-plus/ImageProcessing2/flip.jpg">
</div>
<div class="duplicated"></div>
<div class="portfolio-close">✕</div>
</div>
</div>

ie11 css rotate - background element not clickable

I'm creating a tab interface whereby the active tab has a rotate transformation which allows a preview of the next tab to be visible. Clicking on a button in the preview area will open the next tab.
This is working fine in all browsers apart from IE11. See snippet below.
$(document).ready(function() {
$('button').click(function() {
alert('Button was clicked');
});
})
* {
margin: 0;
padding: 0
}
.container {
width: 400px;
height: 200px;
position: relative;
overflow: hidden;
}
.container .tab {
position: absolute;
top: 0;
left: 0;
width: 185%;
height: 140%;
transform: translateX(-50%) rotate(-25deg);
transform-origin: 50% 0;
overflow: hidden;
}
.container .tab .content {
transform: rotate(25deg);
transform-origin: 0 0;
margin-left: 50%;
position: relative;
height: 75%;
width: 55%;
}
.container .tab-1 {
z-index: 2;
}
.container .tab-1 .content {
background-color: red;
}
.container .tab-2 {
z-index: 1;
height: 200%;
}
.container .tab-2 .content {
background-color: green;
}
.container .tab-2 button {
position: absolute;
bottom: 37%;
right: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="tab tab-1">
<div class="content"></div>
</div>
<div class="tab tab-2">
<div class="content">
<button type="button">
Click me
</button>
</div>
</div>
</div>
I think the problem is that although IE visibly performs the rotation, the original bounding area itself is not rotated, thus the click doesn't get applied on the background element.
Anybody got any ideas how I can fix this? jsfiddle here: https://jsfiddle.net/bmq2e2ae/1/
For this to work in IE you can add pointer-events: none for .tab .content and bring back pointer-events to initial state for children of .tab .content.
Demo:
$(document).ready(function() {
$('button').click(function() {
alert('Button was clicked');
});
})
* {
margin: 0;
padding: 0
}
.container {
width: 400px;
height: 200px;
position: relative;
overflow: hidden;
}
.container .tab {
position: absolute;
top: 0;
left: 0;
width: 185%;
height: 140%;
transform: translateX(-50%) rotate(-25deg);
transform-origin: 50% 0;
overflow: hidden;
}
.container .tab .content {
transform: rotate(25deg);
transform-origin: 0 0;
margin-left: 50%;
position: relative;
height: 75%;
width: 55%;
}
.container .tab-1 {
z-index: 2;
}
.container .tab-1 .content {
background-color: red;
}
.container .tab-2 {
z-index: 1;
height: 200%;
}
.container .tab-2 .content {
background-color: green;
}
.container .tab-2 button {
position: absolute;
bottom: 37%;
right: 20px;
}
/* IE Hack: disable pointer-events */
.container .tab .content {
pointer-events: none;
}
/* IE Hack: enable pointer-events for descendants */
.container .tab .content > * {
pointer-events: initial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="tab tab-1">
<div class="content"></div>
</div>
<div class="tab tab-2">
<div class="content">
<button type="button">
Click me
</button>
</div>
</div>
</div>
Also you wrap this hack in media query browser hack to be evaluated only in IE
#media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
/* IE Hack: disable pointer-events */
.container .tab .content {
pointer-events: none;
}
/* IE Hack: enable pointer-events for descendants */
.container .tab .content > * {
pointer-events: initial;
}
}
When you use CSS3 TRANSFORMS you need to use different tags for diffeent browser.
You need to use like mentioned below.
-webkit-transform: rotate(-25deg) translateX(-50%);
-webkit-transform-origin: 50% 0%;
-moz-transform: rotate(-25deg) translateX(-50%);
-moz-transform-origin: 50% 0%;
-o-transform: rotate(-25deg) translateX(-50%);
-o-transform-origin: 50% 0%;
-ms-transform: rotate(-25deg) translateX(-50%);
-ms-transform-origin: 50% 0%;
transform: rotate(-25deg) translateX(-50%);
transform-origin: 50% 0%;
This will work and supported in below mentioned browser versions.
Chrome : 4.0+
Firefox : 3.5+
Safari : 3.1+
IE : 9.0+
Opera : 10.5+
1st Example from requirement : Instead of rotating both the div you can rotate only second div and it will work. Please check below solution.
$(document).ready(function() {
$('button').click(function() {
alert('Button was clicked');
});
});
* {
margin: 0;
padding: 0
}
.container {
width: 400px;
height: 200px;
position: relative;
overflow: hidden;
}
.container .tab {
width: 185%;
height: 140%;
overflow: hidden;
}
.container .tab .content {
position: relative;
height: 100%;
width: 100%;
}
.container .tab-1 {
z-index: 2;
width: 100%;
}
.container .tab-1 .content {
background-color: red;
}
.container .tab-2 {
z-index: 3;
transform: translateX(-40%) rotate(-25deg);
transform-origin: 50% 0;
}
.container .tab-2 .content {
background-color: green;
}
.container .tab-2 button {
position: absolute;
right: 60px;
top:20px;
transform: translateX(50%) rotate(25deg);
transform-origin: 50% 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="tab tab-1">
<div class="content">
</div>
</div>
<div class="tab tab-2">
<div class="content">
<button type="button">
Click me
</button>
</div>
</div>
</div>
2nd Example from requirement.: Within one div you can achieve this. Please check below.
$(document).ready(function() {
$('button').click(function() {
alert('Button was clicked');
});
});
* {
margin: 0;
padding: 0
}
.container {
position: relative;
overflow: hidden;
}
.container .tab{
z-index: 2;
margin-bottom:10px;
position: relative;
display: block;
width: 400px;
height: 200px;
background-color: red;
overflow: hidden;
}
.container .content {
z-index: 62;
width: 200px;
height: 120px;
background-color: green;
position: absolute;
bottom: -72px;
right: -35px;
transform: rotate(-25deg) ;
-webkit-transform: rotate(-25deg) ;
-moz-transform: rotate(-25deg) ;
-o-transform: rotate(-25deg) ;
-ms-transform: rotate(-25deg) ;
}
.container button{
border:1px solid #eee;
}
.container a {
color:#FFF;
}
.container button,
.container a {
position: absolute;
display: block;
margin-right: 30px;
right: 15px;
bottom: 80px;
transform: rotate(25deg);
-webkit-transform: rotate(25deg);
-moz-transform: rotate(25deg);
-o-transform: rotate(25deg);
-ms-transform: rotate(25deg);
cursor: pointer;
}
<!doctype html>
<html>
<head>
<title>IE10/11 Media Query Test</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="tab tab-1">
Tab With Link
<div class="content">
Link
</div>
</div>
<div class="tab tab-2">
Tab With Button
<div class="content">
<button id="check_btn" type="button">Click me</button>
</div>
</div>
<div class="tab tab-3">
Tab Without Link or Button
<div class="content">
</div>
</div>
<div class="tab tab-4">
Only Tab with no Green Area
</div>
</div>
</body>
</html>
This will help you. Let me know if it not works for you.
The parent element .tab-2 of the button has a lower z-index set then the other .tab-1. So in IE .tab-1 is covering everything, when trying to click even though not visibility.
Take the button element out of the .tab-2 .content elements and place after both inside the container. Then set position:absolute and a higher z-index then both the .container .tab-2 and .container .tab-1. The re-position.
Note: You have to scroll in example below to see button due to elements height.
JSFiddle Demo
$(document).ready(function() {
$('button').click(function() {
alert('Button was clicked');
});
})
* {
margin: 0;
padding: 0
}
.container {
width: 500px;
height: 300px;
position: relative;
overflow: hidden;
}
.container .tab {
position: absolute;
top: 0;
left: 0;
width: 185%;
height: 140%;
transform: translateX(-50%) rotate(-25deg);
transform-origin: 50% 0;
overflow: hidden;
}
.container .tab .content {
transform: rotate(25deg);
transform-origin: 0 0;
margin-left: 50%;
position: relative;
height: 75%;
width: 55%;
}
.container .tab-1 {
z-index: 2;
}
.container .tab-1 .content {
background-color: red;
}
.container .tab-2 {
z-index: 1;
height: 200%;
}
.container .tab-2 .content {
background-color: green;
}
.container button {
position: absolute;
bottom: 5%;
right: 10px;
z-index: 10;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div class="container">
<div class="tab tab-1">
<div class="content">Tab 1</div>
</div>
<div class="tab tab-2">
<div class="content">Tab 2</div>
</div>
<button type="button">Click me</button>
</div>
Instead of rotating the first tab, you could rotate the second one (the one with the button). It would allow to keep that second tab on top of the first one. You can see the result in this jsfiddle.
$(document).ready(function() {
$('button').click(function(e) {
e.stopPropagation();
alert('Button was clicked');
});
$('.tab-1').click(function() {
alert('Tab1 was clicked');
});
$('.tab-2').click(function() {
alert('Tab2 was clicked');
});
})
* {
margin: 0;
padding: 0
}
.container {
width: 400px;
height: 200px;
position: relative;
overflow: hidden;
}
.container .tab {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.container .tab .content {
position: relative;
height: 100%;
background-color: red;
}
.container .tab-2 {
transform: translateX(63%) translateY(100%) rotate(-25deg);
transform-origin: 0% 0%;
overflow: hidden;
}
.container .tab-2 .content {
background-color: green;
transform: rotate(25deg) translateX(-63%) translateY(-100%);
transform-origin: 0% 0%;
}
.container .tab-2 button {
position: absolute;
bottom: 4%;
right: 3%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="tab tab-1">
<div class="content">
Tab 1
</div>
</div>
<div class="tab tab-2">
<div class="content">
Tab 2
<button type="button">
Click me
</button>
</div>
</div>
</div>

scaling a centered div in a scrollable container

I'm trying to do a scale transformation on a div that is centered on a scrollable container div.
The trick i'm using to reflect the new div size after transformation, is using a wrapper and setting the new width/height to it so the parent can show the scrollbars correctly.
.container {
position: relative;
border: 3px solid red;
width: 600px; height: 400px;
background-color: blue;
overflow-x: scroll; overflow-y: scroll;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.wrapper {
order: 1;
background-color: yellow;
}
.content-outer {
width: 300px;
height: 200px;
transform-origin: 50% 50%;
/*transform-origin: 0 0;*/
}
.content-outer.animatted {
animation: scaleAnimation 1s ease-in forwards;
}
.content-outer.animatted2 {
animation: scaleAnimation2 1s ease-in forwards;
}
.content-inner {
width: 300px;
height: 200px;
background: linear-gradient(to right, red, white);
}
if the transformation origin was 0,0 the div is centered without animation jumps but the scrollbars are not correct. if the origin was in the middle both the div location and scrollbars are missed up
I have tried two ways to do the centering, using flexbox (http://jsfiddle.net/r3jqyjLz/1/) and using negative margins
(http://jsfiddle.net/roLf5tph/1/).
Is there a better way to do this ?
Is this what you are after?
I used CSS transitions for the scaling animation.
#centered {
background-image: linear-gradient(to bottom,yellow,red);
height: 200px;
transform: scale(1);
transition: transform 1s ease-out;
width: 200px;
}
#scrollable {
align-items: center;
border: 1px solid red;
display: flex;
height: 300px;
justify-content: center;
overflow: auto;
width: 300px;
}
Update #1
How about this one?
Using old school absolute centering (position absolute)
I see your point about flexbox. It seems that flexbox centering has some limitations when the centered element is larger than its container.
I am assuming that parts of the problem you are having can be summarized as:
You have a complex structure in your inner div which you want to scale as a group. (If it were a single element, this would've been easy with a matrix).
When the inner div is scaled beyond the bounds of the container, you don't get scrollbars without controlling the width/height.
You want the inner div to remain centered and at the same time, scrollbars should reflect the correct position.
With this, there are two (three actually) easy options.
Option 1:
Using the same markup in the question, you could keep the div centered when the scale factor is below 1. And for scale factors above 1, you change it to top-left. the overflow: auto on the container will take care of the scroll on its own because the div being scaled (via transform) is wrapped inside of another div. You don't really need Javascript for this.
This solves your problems 1 and 2.
Fiddle 1: http://jsfiddle.net/abhitalks/c0okhznc/
Snippet 1:
* { box-sizing: border-box; }
#container {
position: relative;
border: 1px solid red; background-color: blue;
width: 400px; height: 300px;
overflow: auto;
}
.wrapper {
position: absolute;
background-color: transparent; border: 2px solid black;
width: 300px; height: 200px;
top: 50%; left: 50%;
transform-origin: top left;
transform: translate(-50%, -50%);
transition: all 1s;
}
.box {
width: 300px; height: 200px;
background: linear-gradient(to right, red, white);
border: 2px solid yellow;
}
.inner { width:50px; height: 50px; background-color: green; }
#s0:checked ~ div#container .wrapper { transform: scale(0.5) translate(-50%, -50%); }
#s1:checked ~ div#container .wrapper { transform: scale(0.75) translate(-50%, -50%); }
#s2:checked ~ div#container .wrapper { transform: scale(1) translate(-50%, -50%); }
#s3:checked ~ div#container .wrapper { top: 0%; left: 0%; transform-origin: top left; transform: scale(1.5) translate(0%, 0%); }
#s4:checked ~ div#container .wrapper { top: 0%; left: 0%; transform-origin: top left; transform: scale(2) ; }
#s5:checked ~ div#container .wrapper { top: 0%; left: 0%; transform-origin: top left; transform: scale(3) ; }
<input id="s0" name="scale" data-scale="0.5" type="radio" />Scale 0.5
<input id="s1" name="scale" data-scale="0.75" type="radio" />Scale 0.75
<input id="s2" name="scale" data-scale="1" type="radio" checked />Scale 1
<input id="s3" name="scale" data-scale="1.5" type="radio" />Scale 1.5
<input id="s4" name="scale" data-scale="2" type="radio" />Scale 2
<input id="s5" name="scale" data-scale="3" type="radio" />Scale 3
<hr>
<div id="container">
<div id="wrapper" class="wrapper">
<div id="box" class="box">
<div class="inner"></div>
</div>
</div>
</div>
But this creates another problem. The overflow:auto will cause a jump/flicker when the div is scaled beyond the container. This can be easily solved by making it overflow:scroll to show the scrollbars at all times (the way you are doing it already). Although, it works like a charm in Firefox, Chrome falters here and doesn't update the scrollbar position. The trick here is to use Javascript to force a reflow by changing the overflow to auto once your scaling completes. So you need to delay it a bit.
Fiddle 2: http://jsfiddle.net/abhitalks/u7sfef0b/
Snippet 2:
$("input").on("click", function() {
var scroll = 'scroll',
scale = +($(this).data("scale"));
if (scale > 1) { scroll = 'auto'; }
setTimeout(fixScroll, 300, scroll);
});
function fixScroll(scroll) { $("#container").css({ overflow: scroll }); }
* { box-sizing: border-box; }
#container {
position: relative;
border: 1px solid red; background-color: blue;
width: 400px; height: 300px;
overflow: scroll;
}
.wrapper {
position: absolute;
background-color: transparent; border: 2px solid black;
width: 300px; height: 200px;
top: 50%; left: 50%;
transform-origin: top left;
transform: translate(-50%, -50%);
transition: all 1s;
}
.box {
width: 300px; height: 200px;
background: linear-gradient(to right, red, white);
border: 2px solid yellow;
}
.inner { width:50px; height: 50px; background-color: green; }
#s0:checked ~ div#container .wrapper { transform: scale(0.5) translate(-50%, -50%); }
#s1:checked ~ div#container .wrapper { transform: scale(0.75) translate(-50%, -50%); }
#s2:checked ~ div#container .wrapper { transform: scale(1) translate(-50%, -50%); }
#s3:checked ~ div#container .wrapper { top: 0%; left: 0%;transform-origin: top left; transform: scale(1.5) translate(0%, 0%); }
#s4:checked ~ div#container .wrapper { top: 0%; left: 0%;transform-origin: top left; transform: scale(2) ; }
#s5:checked ~ div#container .wrapper { top: 0%; left: 0%;transform-origin: top left; transform: scale(3) ; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="s0" name="scale" data-scale="0.5" type="radio" />Scale 0.5
<input id="s1" name="scale" data-scale="0.75" type="radio" />Scale 0.75
<input id="s2" name="scale" data-scale="1" type="radio" checked />Scale 1
<input id="s3" name="scale" data-scale="1.5" type="radio" />Scale 1.5
<input id="s4" name="scale" data-scale="2" type="radio" />Scale 2
<input id="s5" name="scale" data-scale="3" type="radio" />Scale 3
<hr>
<div id="container">
<div id="wrapper" class="wrapper">
<div id="box" class="box">
<div class="inner"></div>
</div>
</div>
</div>
Option 2:
In order to solve problem 3 of keeping the div centered and at the same time maintaining the correct scrollbar position, you have to fallback on Javascript.
The principle remains the same that for scale factors above 1 you need to reset the top-left and translate positions. You would also need to recalc the scaled width/height and then re-assign that to your wrapper div. Then setting the scrollTop and scrollLeft on the container will be as easy as just getting the difference of the wrapper div and the container div.
This solves your problems 1, 2, and 3.
Fiddle 3: http://jsfiddle.net/abhitalks/rheo6o7p/1/
Snippet 3:
var $container = $("#container"),
$wrap = $("#wrapper"),
$elem = $("#box"),
originalWidth = 300, originalHeight = 200;
$("input").on("click", function() {
var factor = +(this.value);
scaler(factor);
});
function scaler(factor) {
var newWidth = originalWidth * factor,
newHeight = originalHeight * factor;
$wrap.width(newWidth); $wrap.height(newHeight);
if (factor > 1) {
$wrap.css({ left: 0, top: 0, transform: 'translate(0,0)' });
$elem.css({ transform: 'scale(' + factor + ')' });
setTimeout(setScroll, 400);
} else {
$elem.css({ transform: 'scale(' + factor + ') ' });
$wrap.css({ left: '50%', top: '50%', transform: 'translate(-50%, -50%)' });
}
}
function setScroll() {
var horizontal, vertical;
horizontal = ($wrap.width() - $container.width()) / 2;
vertical = ($wrap.height() - $container.height()) / 2;
$container.stop().animate({scrollTop: vertical, scrollLeft: horizontal}, 500);
}
* { box-sizing: border-box; }
#container {
position: relative;
border: 1px solid red; background-color: blue;
width: 400px; height: 300px;
overflow: scroll;
}
.wrapper {
position: absolute;
background-color: transparent; border: 2px solid black;
width: 300px; height: 200px;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
transition: all 0.5s;
}
.box {
width: 300px; height: 200px;
background: linear-gradient(to right, red, white);
border: 2px solid yellow;
transform-origin: top left;
transition: all 0.5s;
}
.inner { width:50px; height: 50px; background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<label for="slider1">Scale: </label>
<input id="slider1" type="range" min="0.5" max="3" value="1" step="0.25" list="datalist" onchange="scaleValue.value=value" />
<output for="slider1" id="scaleValue">1</output>
<datalist id="datalist">
<option>0.5</option>
<option>0.75</option>
<option>1</option>
<option>1.5</option>
<option>2</option>
<option>3</option>
</datalist>
<hr>
<div id="container">
<div id="wrapper" class="wrapper">
<div id="box" class="box">
<div class="inner"></div>
</div>
</div>
</div>
All scripts tested with IE-11, GC-43, and FF-38
.
The hard part of this problem is that, in the middle of the zoom in process, you need to change from a model (where you have still margins) to another model where you need to expand the size of the container.
Since this happens in the middle of the transform, it's difficult to handle with a single property transform
The way that I have found to solve this is to change the min-height and min-width properties. This will give the posibility to auto detect this point and handle it gracefully
I am keeping in JS only the basic functionality, everything else is done in CSS
function setZoom3() {
var ele = document.getElementById("base");
ele.className = "zoom3";
}
function setZoom1() {
var ele = document.getElementById("base");
ele.className = "";
}
function setZoom05() {
var ele = document.getElementById("base");
ele.className = "zoom05";
}
.container {
width: 300px;
height: 300px;
overflow: scroll;
position: relative;
}
#base {
width: 300px;
height: 300px;
top: 0px;
left: 0px;
position: absolute;
min-width: 300px;
min-height: 300px;
transition: min-width 5s, min-height 5s;
}
.inner {
width: 200px;
height: 200px;
background-color: lightgreen;
background-image: linear-gradient(-45deg, red 5%, yellow 5%, green 95%, blue 95%);
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
transform-origin: top left;
transition: transform 5s;
}
#base.zoom3 {
min-width: 600px;
min-height: 600px;
}
.zoom3 .inner {
transform: scale(3) translate(-50%, -50%);
}
.zoom05 .inner {
transform: scale(0.5) translate(-50%, -50%);
}
.trick {
width: 20px;
height: 20px;
background-color: red;
position: absolute;
transform: translate(0px);
}
<div class="container">
<div id="base">
<div class="inner"></div>
</div>
</div>
<button onclick="setZoom3();">zoom 3</button>
<button onclick="setZoom1();">zoom 1</button>
<button onclick="setZoom05();">zoom 0.5</button>
The only issue left would be to set the scroll position, just in case you want it to keep in the center
I've managed to solve it with a touch of logic. I've used GSAP for the animation and updates, but you could easily get it working with vanilla JS:
http://codepen.io/theprojectsomething/pen/JdZWLV
... Because of the need to scroll the parent div to keep the element centred (and no way to animate the scroll) you're not going to be able to get a smooth transition with CSS alone.
Note: Even without the scroll, a pure CSS transition seems to have trouble syncing (the offset, whether top/left, margin, or translate, is always catching up with the scale) ... this may be due to sub-pixel positioning? Someone else may be able to provide further insight here.
Full code from Codepen:
var $inner = document.querySelector('aside'),
$outer = document.querySelector('main'),
$anim = document.querySelector('[type="checkbox"]'),
$range = document.querySelector('[type="range"]'),
data = {
width: $outer.clientWidth,
value: 1
};
$range.addEventListener('input', slide, false);
function slide () {
$anim.checked ? animate(this.value) : transform(this.value);
}
function animate (value) {
TweenLite.to(data, 0.4, {
value: value,
onUpdate: transform
});
}
function transform (value) {
if( !isNaN(value) ) data.value = value;
var val = Math.sqrt(data.value),
offset = Math.max(1 - val, 0)*0.5,
scroll = (val - 1)*data.width*0.5;
TweenLite.set($inner, {
scale: val,
x: offset*100 + "%",
y: offset*100 + "%"
});
TweenLite.set($outer, {
scrollLeft: scroll,
scrollTop: scroll
});
}
window.onresize = function (){
data.width = $outer.clientWidth;
transform(data.value);
};
main, aside:before, footer {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
main {
width: 50vh;
height: 50vh;
min-width: 190px;
min-height: 190px;
background: black;
overflow: scroll;
box-sizing: border-box;
}
aside {
position: relative;
width: 100%;
height: 100%;
border: 1vh solid rgba(0, 0, 0, 0.5);
background-image: -webkit-linear-gradient(top, yellow, red);
background-image: linear-gradient(to bottom, yellow, red);
box-sizing: border-box;
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
aside:before {
content: "";
background: black;
border-radius: 50%;
width: 10%;
height: 10%;
display: block;
opacity: 0.5;
}
input {
display: block;
margin: 3em auto;
}
input:after {
content: attr(name);
color: white;
text-transform: uppercase;
position: relative;
left: 2em;
display: block;
line-height: 0.9em;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js"></script>
<main>
<aside></aside>
</main>
<footer>
<input type="checkbox" name="animate" checked>
<input type="range" min="0.1" step="0.005" max="3">
</footer>
Matthew King's fiddle was close to being correct.The only thing that needs fixing is the div's offset. If your inner div is exceeding the boundaries of the scrollable div (negative offset) you need to set the offset to 0. Else you want the div to be centered. Centering is however not that trivial. You are zooming the div's background with css, hence you are not affecting the actual width and height of your div and need to parse the scale matrix to calculate it's backgrounds dimension.
This solution works for me: https://jsfiddle.net/6e2g6vzt/11/ (at least for FF on Ubuntu, did not test on other browsers yet)It is basically just one function to set the new offset, you don't even have to call it manually due to this line of code
$("#centered").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", setNewOffset);
which calls the setNewOffset function after the transformation is completed.
As you can see the image 'jumps' to it's correct position after the transformation, maybe you want to add a smooth effect to cover that but i just wanted to show you how to get the correct offset.
Have a look at jQuery's documentation to learn more about .offset()
Credits:
Thanks to Jim Jeffers for the nice callback after transistions https://stackoverflow.com/a/9255507/3586288
Thanks to Lea Verou for the great regex to parse the scale matrix https://stackoverflow.com/a/5604199/3586288

Categories

Resources