I have a drawer menu that appears on screen after clicking a button. The transition is a little laggy using absolute positioning so I went with using translate3d. The menu has two widths. One that's 100% of the view for mobile and 395px for viewports 600px and up. One quirk I'm noticing is that if I were to resize my screen, the menu will subtly appear. Is there a way to get the menu to completely stay off-screen when this happens?
Note: To see this, expand the snipped to full-screen and horizontally resize to 600px or lower. You should see see the div appear and go back off-screen.
$('button').on('click', function() {
$('#drawer').toggleClass('active');
});
html,
body {
height: 100%;
margin: 0;
}
#drawer {
width: 100%;
height: 100%;
background-color: grey;
transition: transform .5s ease-in;
transform: translate3d(-100%, 0, 0);
}
#drawer.active {
transform: translate3d(0, 0, 0);
}
#media (min-width: 600px) {
#drawer {
transform: translate3d(-395px, 0, 0);
width: 395px;
}
}
button {
position: absolute;
top: 10px;
left: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="drawer"></div>
<button>Toggle Drawer</button>
The problem: A transition is set on the drawer's transform property. When it hits the designated media query, the drawer transitions to it's new x coordinate.
The solution: Instead of putting the responsibility of placement solely on transform, use absolute positioning and initially offset the drawer to its' negative width. In this case it would be 395px. On the active state, the transform3d property would be the width of the drawer to bring it back into the visible part of the window.
$('button').on('click', function() {
$('#drawer').toggleClass('active');
});
html,
body {
height: 100%;
margin: 0;
}
#drawer {
width: 100%;
height: 100%;
position: absolute;
left: -100%;
background-color: grey;
transition: transform .5s ease-in;
transform: translate3d(0,0,0);
}
#drawer.active {
transform: translate3d(100%, 0, 0);
}
#media (min-width: 600px) {
#drawer {
left: -395px;
width: 395px;
}
#drawer.active {
transform: translate3d(395px, 0, 0);
}
}
button {
position: absolute;
top: 10px;
left: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="drawer"></div>
<button>Toggle Drawer</button>
Related
How do you make the card to transform back to the front card when you click on the return arrow left icon at the back of this card both on mobile and desktop device. https://jsfiddle.net/86pkrs4v/. I tried using the ff breakpoints but it won't transform. I still wan't the hover to be applied in desktop and on mobile you can click anywhere of the front card to apply the transform styling.
#media (min-width:1025px) {
.more {
display: none;
}
.card:hover .content {
transform: rotateY(180deg);
}
}
#media only screen
and (min-device-width : 320px)
and (max-device-width : 1024px) {
.more {
display: none;
}
.more:checked ~ .content {
transform: rotateY(180deg) !important;
}
.card:hover .content {
transform: rotateY(180deg) !important;
}
}
you can add the following javascript:
$(".card").click(function(e){
$(".card").addClass("myclass");
});
$(".card").mouseout(function(e){
$(".card").removeClass("myclass");
})
and the class in css:
.myclass .content{
transform: rotateY(0deg)!important;
}
in this example you can click anywhere on the card to make the rotation back to 0 degrees, but you can change it to
$(".button").click(function(e){ ...
if you only want the arrow to be clickable.
This is your solution if you want then you can put some contents as per your requirement inside the card
var card = document.querySelector('.card');
card.addEventListener( 'click', function() {
card.classList.toggle('is-flipped');
});
.scene {
width: 200px;
height: 260px;
margin: 40px 0;
perspective: 600px;
}
.card {
width: 100%;
height: 100%;
transition: transform 1s;
transform-style: preserve-3d;
cursor: pointer;
position: relative;
}
.card.is-flipped {
transform: rotateY(180deg);
}
.card__face {
position: absolute;
width: 100%;
height: 100%;
line-height: 260px;
color: white;
text-align: center;
font-weight: bold;
font-size: 40px;
backface-visibility: hidden;
}
.card__face--front {
background: red;
}
.card__face--back {
background: blue;
transform: rotateY(180deg);
}
<div class="scene scene--card">
<div class="card">
<div class="card__face card__face--front">front</div>
<div class="card__face card__face--back">back</div>
</div>
</div>
edit or preview Here on JSFiddle
If I understand, clicking on arrow you want it to behave same as when you remove mouse away from <div class="content">.
If so, there are two things:
make it transform: rotateY(0deg), not 180 since it is already rotated for 180 degrees and we want it to turn back.
Clean it up for hover to work and let css work as before.
I think, the simplest solution is
let backArrowLeft = document.querySelector(".button.return");
let flipContent = document.querySelector(".content");
backArrowLeft.addEventListener("click", function() {
flipContent.style = "transform: rotateY(0deg);";
});
flipContent.addEventListener("mouseout", function() {
this.style = "";
});
To make it easier to maintain, you can create class and add/remove it on those two events, similar to Joachim's answer, I just post it since I've seen on fiddle that you don't use jQuery.
Let's mark the discover button first:
<label for="card1" class="button discover" aria-hidden="true">
Discover
</label>
You can add the following in the javascript. Note that a click on mobile will also activate the hover trigger, so we don't just want to toggle based on a click on the button:
$(".return").click(function(e){
$(".card").addClass("turned");
});
$(".discover").click(function(e){
$(".card").removeClass("turned");
});
Then, replace in the CSS the transformation:
.card:not(.turned):hover .content {
transform: rotateY(180deg);
}
.card.turned .content {
transform: rotateY(180deg);
}
.card.turned:hover .content {
transform: rotateY(0deg);
}
I want to use a CSS transform to animate a video to display on a page, but I want the video to push the content down as it animates in, instead of the default situation where the space of the video already exists within the UI.
I created a jsfiddle to show what I mean here: https://jsfiddle.net/njpatten/198sh5ec/2/
I prefer to only transform the element for performance reasons, but I've tried modifying height as well to get the desired effect, but the result is a bit jumpy, and also not as performant as I'd like.
.video {
transform-origin: 0 0;
transform: scale(0, 0);
transition: 0.2s;
height: 0;
&.open {
transform: scale(1, 1);
height: 100%;
}
}
Is there a 'hack' that I'm missing that could solve this issue? How do animators deal with the element taking up space when trying to animate in an element while still keeping things performant (and therefore only animating transforms and opacity).
Thanks in advance!
Unfortunately, transform isn't going to cut it. It can't affect the layout of the document, which is what you're trying to achieve with pushing the text copy down. That said, I would approach this by first making the iframe container set its own dimensions based on an aspect ratio. Your video is at 560x315 which calculated as a percentage is 56.25% as tall as it is wide. The percentage is needed because it can be used with the "padding trick" to get an element that scales according to aspect ratio. So the first part is to make your video container based on aspect ratio:
.video {
position: relative;
max-width: 560px;
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
&:after {
content: '';
display: block;
padding-top: 56.25%;
}
}
As you'll notice you can now set the width of .video and it will always have the right height for the video. Next we need to add the transition. We'll be transitioning the padding-top property.
.video {
position: relative;
max-width: 560px;
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
&:after {
content: '';
display: block;
padding-top: 0%;
transition: padding 0.2s;
}
&.open:after {
padding-top: 56.25%;
}
}
Lastly, you can add a transition to your video so it scales up instead of opens like a shade:
.video {
position: relative;
max-width: 560px;
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: transform 0.2s;
transform-origin: 0 0;
transform: scale(0,0);
}
&:after {
content: '';
display: block;
padding-top: 0%;
transition: padding 0.2s;
}
&.open:after {
padding-top: 56.25%;
}
&.open iframe {
transform: scale(1,1);
}
}
The end result is similar but not exact to what you were wanting:
https://jsfiddle.net/jmarikle/kjd87was/
I am using purely CSS to create a dynamically opening and closing sidebar based on the view-port with of the page. I have a couple issues with my code however:
How can I prevent an animation when the screen first loads? That is, I simply want the sidebar to be opened or closed on first loading, and then animated when the view-port is adjusted.
Why do I have to have two separate animations? Notice I have two identical keyframes toggle and toggle1, which are used for closing and opening respectively. If I try to use just toggle for both animations, the animation occurs instantly. Any workaround without duplicated code?
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
}
#media (min-width: 500px) {
#sidebar {
animation: toggle 200ms ease-in 1 reverse forwards;
}
}
#media (max-width: 500px) {
#sidebar {
animation: toggle1 200ms ease-out 1 normal forwards;
}
}
#keyframes toggle {
0% {
left: 0px;
}
100% {
left: -200px;
}
}
#keyframes toggle1 {
from {
left: 0px;
}
to {
left: -200px;
}
}
<div id="sidebar"></div>
Just use a simple transition instead of animation, and 1 media query
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
transition:left 500ms ease;
left:0;
}
#media (max-width: 500px) {
#sidebar {
left: -200px;
}
}
https://jsfiddle.net/ufoste1y/3/
You can also use transform:translateX, which should provide better performance.
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
transition:transform 500ms ease;
}
#media (max-width: 500px) {
#sidebar {
transform: translateX(-200px);
}
}
https://jsfiddle.net/ufoste1y/8/
In order to force the sidebar to be opened or closed by default, that cannot be done with raw CSS. You'd need JavaScript to wait until the page has finished loading, and then dynamically update the element to have a class or similar that the CSS animation applies to.
As for the duplicate code, you can certainly just use toggle. It not working for you was probably a result of you forgetting to also change the #media animation reference from toggle1 to toggle.
Here's an example showing the sidebar staying open by default with the aid of JavaScript. The animation is now triggering only on the .loaded class, and the JavaScript applies the loaded class to the element 1000 milliseconds after the page has loaded, meaning that it won't trigger the animation initially.
Having said that, you'll probably only want to trigger the slide on some sort of condition anyway, and JavaScript would be much better suited to that :)
window.addEventListener("load", function() {
setTimeout(function() {
var sidebar = document.getElementById("sidebar");
sidebar.classList.add("loaded");
}, 1000);
});
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#sidebar {
position: absolute;
width: 200px;
background-color: #123456;
height: 100vh;
}
#media (min-width: 500px) {
#sidebar.loaded {
animation: toggle 200ms ease-in 1 reverse forwards;
}
}
#media (max-width: 500px) {
#sidebar.loaded {
animation: toggle 200ms ease-out 1 normal forwards;
}
}
#keyframes toggle {
0% {
left: 0px;
}
100% {
left: -200px;
}
}
<div id="sidebar"></div>
I have a large element that has multiple animated (rotating) images, and you can zoom in and out on the entire div (which changes its scale transform). However, if an image element is created while the div is zoomed out, when I zoom back in I can see that the image is very blurry, as if it the image was downscaled when the element was created. A possible workaround would be to hide & show the image every time I zoom, but that doesn't sound like the best solution.
Here's a snippet demonstrating the issue (fiddle). Click on the first link to get a blurred image (sometimes only breaks on the second click), and on the second link to get a good image.
$(".try-1").click(function() {
$(".image").remove();
$(".pos").css("transform", "scale(0.4)").append("<div class=\"image\"></div>");
setTimeout(() => {
$(".pos").css("transform", "scale(1.4)");
}, 500)
});
$(".try-2").click(function() {
$(".image").remove();
$(".pos").css("transform", "scale(1.4)").append("<div class=\"image\"></div>");
});
.clicky {
color: #00f;
cursor: pointer;
}
.clicky:hover {
text-decoration: underline;
}
.div {
position: relative;
width: 500px;
height: 500px;
background: #000;
}
.pos {
position: absolute;
left: 250px;
top: 250px;
}
#keyframes rotating-cw {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.image {
position: absolute;
left: -150px;
top: -150px;
width: 300px;
height: 300px;
background: url(http://i.imgur.com/grJ6I3k.png);
background-size: 300px 300px;
animation: rotating-cw 30s linear infinite;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="clicky try-1">Create & zoom in</span> | <span class="clicky try-2">Zoom in & create</span>
<div class="div">
<div class="pos">
</div>
</div>
use this:
.pos {
-webkit-perspective: 1000;
position: absolute;
left: 250px;
top: 250px;
}
This can be solved using requestAnimationFrame but a simpler and more straightforward solution is to tell the browser to again initialize the image's container
$(".try-1").click(function() {
$(".image").remove();
$(".pos").css("transform", "scale(0.4)").append("<div class=\"image\"></div>");
setTimeout(() => {
$(".pos").css("transform", "scale(1.4)");
// Here, after we scale up the element again append
// the .image element but first remove it
$(".image").remove();
$(".pos").append("<div class=\"image\"></div>");
}, 500)
});
JsFiddle
I have a div sized in pixels, and centered on screen with the top/left+transform trick. I've created a CSS animation to animate this box to fill the entire screen, but since it's based on height/width, the framerate is awful.
Here is a demo of what I've achieved. While the framerate is decent enough in the fiddle, in my actual use case with many other elements on the page, the framerate is terrible.
https://jsfiddle.net/ozxyfuje/
I want to convert this to use transform, but I'm not sure how to go about it, especially considering the transform that's already there to center it.
I tried to use calc() to figure out the scale factor from the dimensions of the div and the viewport, but apparently vw and vh are not compatible with calc().
Ideally I'd like to avoid JavaScript, but I can accept a JavaScript based solution as a last resort.
To avoid script you need to work with viewport units all the way, and then use scale()
Do note, to achive better perfomance don't use transition: all 0.5s, name which properties to be transitioned, like transition: transform 0.5s;
$("#hello").click(function(){
$("body").toggleClass("boom");
});
body {
background: #222;
/* full screen app */
width: 100%;
height: 100%;
overflow: hidden;
}
#hello {
background: #b00;
/* pixel size set in stone */
width: 40vw;
height: 40vh;
/* center on screen */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: transform 0.5s;
}
.boom #hello {
transform: translate(-50%, -50%) scale(2.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="hello">
</div>
</body>
As far as I know it is not possible to achieve it using only CSS while maintaining a fixed width and height (not dependant on the width/height of the container).
You can achieve it using some JavaScript (jQuery in this case). I am adding the class 'full' to know if the element is full size or not, but you could achieve it with a boolean variable for example.
JSFiddle
$("#hello").click(function() {
if ($("#hello").hasClass('full')) {
$("#hello")
.removeClass('full')
.css({
"transform": "translate(-50%, -50%)"
});
} else {
$("#hello")
.addClass('full')
.css({
"transform": "translate(-50%, -50%) scale(" + $(window).width() / 200 + ", " + $(window).height() / 160 + ")"
});// 200 & 160 is the div width & height in pixels, set in the CSS
}
});
body {
background: #222;
/* full screen app */
width: 100%;
height: 100%;
overflow: hidden;
}
#hello {
background: #b00;
/* pixel size set in stone */
width: 200px;
height: 160px;
/* center on screen */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* animation */
transition: 0.5s all;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="hello">
</div>