Translate an element along one axis while keeping the other as is - html

I have a HTML element which has transform: translateX(some_percent) applied.
Is there a way to animate this element along the y-axis without changing the translation along the x-axis?
//EDIT
To clarify, some_percent is unknown and there is no way of accessing it.
I am interested if this is possible in general, without changing the html. It seems to me as if a translation is always a vector of (x,y) with translateY just being a shorthand for translate(0,y).

You could use a wrapping element and animate that:
HTML
<div id="outter">
<div id="img">
</div>
</div>
CSS
#img {
width: 250px;
height: 150px;
background-image: url('http://placehold.it/250x150');
transform: translateX(22%);
}
#outter {
transform: translateY(40%);
}
JS
var transY = 10;
var times = 0;
var x = function() {
if (times > 5) {
times = 0;
}
document.getElementById("outter").style.transform = 'translateY(' + times * transY + '%)';
times++;
setTimeout(x, 1000);
};
x();
https://jsfiddle.net/nayk1uLz/2/

the first parameter is the X axis and the second the Y ;).
transform: translate(50%, 50%);
Here is a jsfiddle : https://jsfiddle.net/fugfswmt/

Here's my solution proposal using a CSS Custom Property (a.k.a CSS Variable):
div {
width: 5rem;
height: 5rem;
text-align: center;
line-height: 5rem;
transition: transform 300ms;
}
#red {
background-color: red;
--some_percent: 24%; /* Default value */
transform: translateX(var(--some_percent)); /* Unknown value */
}
#red.translate-y {
transform: translateX(var(--some_percent)) translateY(50%); /* Any value */
}
<!-- Adding a click event just for demo purposes -->
<div id="red" style="--some_percent: 42%" onclick="this.classList.toggle('translate-y')">Click Me</div>
I'm guessing that if your some_percent value is unknown, you're setting it inline (after some server-side calculation, maybe?), so a pure CSS solution could be to set this unknown value as a CSS variable inline and use it as a constant on translateX and adjust translateY to whatever value you need.

Related

Inherit color but override opacity/transparency

Is it possible to inherit the color but override the opacity value? Here's an example in pseudo CSS:
.color-class {
background-color: rgba(255, 0, 0, 0);
}
.lighten {
background-color: rgba(inherit, inherit, inherit, .4);
}
Applied to
<div style="color-class">I am red</div>
<div style="color-class lighten">
I am red and a little bit transparent
</div>
should result in a colored element and the other one being the same (inherited) color but with added transparency.
I basically want to have a CSS class that lightens (or darkens) the background color without changing the color value.
A solution is to use pseudo element as background and simply control its opacity :
div {
padding: 40px;
}
.color-class {
position: relative;
}
.color-class:before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgb(255, 0, 0);
opacity: 1;
z-index:-1;
}
.lighten:before {
opacity: 0.5;
}
<div class="color-class">I am red</div>
<div class="color-class lighten">
I am red and a little bit transparent
</div>
Firstly, this would not work because inherit takes the value from an elements parent element not its sibling element. See: https://developer.mozilla.org/en-US/docs/Web/CSS/inherit
Secondly, the inherit keyword is a property-value. It cannot be used as an argument to a browser function (rgba) as the function itself is a property-value.
You also don't need to try this hard to enforce inheritance and re-usability, it will often bloat your code and make it over-engineered.
You would ideally have a set colour scheme and therefore be able to choose a fixed colour for lighten, considering you would be re-using this there isn't that much need to try and make it dynamic like that.
Other options you can go for if you REALLY want this functionality are using SASS/SCSS mix-ins to mimic this inheritance, JavaScript could also be an alternative however for what you're trying to achieve this would be really in-efficient.
You can do it, just use CSS variables. Let's say you have element with set background and you want to change its opacity.
<div class="bg-yellow">Yellow div</div>
Add new class for changing opacity:
<div class="bg-yellow bg-opacity-20">Yellow div</div>
And in your CSS:
.bg-yellow {
--bg-opacity: 1;
background-color: rgba(255,230,0, var(--bs-opacity));
}
.bg-opacity {
--bg-opacity: 0.2;
}
"255,230,0" is yellow btw...
And that's it, you can also manipulate this method as you want according to your needs.
.bg-yellow {
--bg-opacity: 1;
background-color: rgba(255,230,0,var(--bg-opacity));
}
.bg-opacity-20 {
--bg-opacity: 0.2;
}
<div class="bg-yellow bg-opacity-20">Yellow div</div>

How to animate sections in pure CSS when scrolling the page?

I am looking for a way to animate (with #keyframes, transform...) some elements when the user scrolls the page. For example:
When offset is 0: div has height: 100px.
When offset is between 0 and 100: div is height: 50px and color: blue.
And so on...
Is is possible using pure CSS?
If it is not, what are the most efficient ways to do it with HTML or Javascript?
The most efficient way to animate an element's style properties depending on scroll position will probably be to add a class with a scroll function:
Working Example
myID = document.getElementById("myID");
var myScrollFunc = function() {
var y = window.scrollY;
if (y > 500) {
// myID.style.backgroundColor = "blue"; // you can add individual styles
myID.className = "blue" // or add classes
} else {
// myID.style.backgroundColor = "red";
myID.className = "red"
}
};
window.addEventListener("scroll", myScrollFunc);
body {
height: 1100px;
}
#myID {
position: fixed;
height: 100px;
line-height: 20px;
transition: all 1s;
}
.blue {
background: blue;
animation: myAnimation 1s both;
}
.red {
background: red;
}
#keyframes myAnimation {
0% {
border-radius: 0px;
line-height: 10px;
}
100% {
border-radius: 100%;
line-height: 100px;
}
}
<div id="myID" class="red">Hello world</div>
Docs:
.scrollY
.className
.addEventListener
Methinnks it's not possible to 'spy' scroll with pure css. If you want, you can do this with jQuery:
$(document).scroll(function() {
var pos = parseInt($(document).scrollTop())
if(pos == 0) {
$('.yourDivClass').css({
height : '100px' ,
color : '#fff'
})
}
if (pos > 0 && pos <= 100) {
$('.yourDivClass').css({
height : '50px' ,
color : 'blue'
})
}
console.log(pos)
})
and of course if you wanna get a smooth transition, you supposed to declare transitions in your css file
.yourDivClass {
transition: height 0.5s ease
}
Scrolling is an event. Once you scroll the page, the event gets triggered and something happens. You cannot control events using Pure CSS. Period.
Some people would argue that even :hover is an event. Yes, and it is for some strange reason, implemented in CSS, but not others.
With pure CSS: no.
But you can have a class with keyframed animation associated with it, and then say when the element is scrolled into view, to add the class to the element. This will make it start doing the animation.
You can use Waypoints.js to set what happens when you reach a specific element of a page.

How to rotate text and position it properly? (CSS, HTML)

I am trying to position a rotated headline next to some text. Statically it works very easy with absolute positioning (left picture). I have, however, difficulties when the page gets resized and the positioning fails (right picture).
Current CSS (can be changed):
.headline {
white-space: nowrap;
position: absolute;
top: 185px;
left: -20px;
transform: rotate(270deg);
}
Current HTML structure (can be changed):
<header>
<h1 class="headline">Über mich</h1>
</header>
<div class="text">
<p class="introduction">....</p>
</div>
How can I position the element so that I always stays 20px next to the paragraph?
Could someone link me to existing patterns how to solve this?
A solution with JS (and jQuery) would an option, I would, however, obviously prefer CSS.
Had the same issue. Managed to solve it like this in pure CSS :
.parent {
position: relative;
}
.child {
position: absolute;
transform: translateX(-100%) rotate(-90deg);
transform-origin: right;
left: 30px; /* change this value to match needs */
top: 30px; /* change this value to match needs */
}
The solution was a combination of Diego's answer and Smamatti's comment:
I had to use transform-origin:bottom and width:0. That was working rather quickly, the big problem I had was positioning the text independently to the length of the text. I've only managed to do this by using javascript.
.header
{
margin: 0;
width: 0;
white-space: nowrap;
position: absolute;
text-align: left;
transform-origin: bottom;
margin-top: 280px;
transform: rotate(270deg);
}
Javascript (to ensure compatibility to variables text length):
function adjustSideHeader() {
//check if the headline is visible
if($('h1.headline').length > 0)
{
var headline = $('h1.headline')[0];
var stringLength = headline.textContent.length;
//add style tag to support media queries
document.querySelector('style').textContent +=
"h1.headline { margin-top: " + (stringLength*23.5) + "px; -webkit-transition: margin-top 2s; transition: margin-top 2s;}"
}
}
// fire it when document is loaded
$(document).ready(setTimeout(adjustSideHeader, 300));
Result:
Have you tried moving
<h1 class="headline">Über mich</h1>
inside
<div class="text">?
and set
.text {
position: relative;
}
so that the position is relative to to "text" div. After that you might want to move the Über mich text to the left by reducing it's left value.
Have you tried use position:relative and the margin property?, I suppose it would be something like this:
.headline {
white-space: nowrap;
position: relative; //changed
margin-top: 185px; //changed
margin-left: -20px; //changed
transform: rotate(270deg);
}
*Note: I think you should move the headline inside the paragraph
I have an answer that may be late but worked wonderfully for me.
Normally your text will have a class or id and it will be position:absolute, and positioning values after it, like so:
.TextClass{
position:absolute;
top:50%;
left:55%;
transform:rotate(-90deg);
etc.
However, when you rotate, the positioning becomes relative (as mentioned above).
I found out that by simply putting the rotated text inside a parent div, you can position the (unrotated, position absolute) parent div as much as you want, and then rotate the text (which will be position:relative) inside the parent div, like so:
.divname{
position:absolute;
top:50vh;
left:50vw;
}
.TextClass{
position:relative;
transform:rotate(-90deg);
}

In pure CSS, is it possible to set margin equal to height of current element?

I'm have a vertical stack of items to which the user can append one by clicking a button, roughly like this.
<ol>
<li><textarea></textarea></li>
<li><textarea></textarea></li>
</ol>
<a data-action="additem">Add another</a>
I'm trying to write a CSS animation so that when the new li is inserted, the "Add another" smoothly slides down to its new resting place. Fixed height on the li tags is not an option, and I'm trying to avoid using the max-height animation hack because it can have weird layout effects.
I figured out that I could animate margin-bottom from something to 0 and have the desired effect, but I can't figure out how in CSS to express that I want the current height of the element to which this rule is applied. Percentages are measured relative to the width of the element, which isn't what I need here, and I can't think of a clever trick using calc or the like to express what I want to the browser.
Suggestions?
EDIT
I'm using a template with a repeat binding to add the items to the list. The JS only pushes another object into an observable array, and the framework handles the actual DOM insertion. The li tag has on it the following CSS to get it to enter smoothly:
animation: append forwards .5s;
And append is defined as:
#keyframes append {
from {
transform: translateX(10%);
opacity: 0;
margin-bottom: _____;
}
to {
transform: none;
opacity: 1;
margin-bottom: 0;
}
}
Not currently...
I've come up against this frustrating issue a number of times, always trying to either animate a non-numeric value, access a specific property of the current element as an animation value, or animate an unspecified value to a specified one. Generally I always have to fall back to either some form of not-quite-perfect max-height animation (like you've already mentioned) or use a mixture of CSS and JavaScript/jQuery.
For your issue there are a few options, but none are exactly what you're after.
css only version (using duplicated markup and another animation)
http://jsfiddle.net/7m8F9/2/
http://jsfiddle.net/7m8F9/3/ <-- improved version using bottom and position:relative
http://jsfiddle.net/7m8F9/5/ <-- even better version, going back to translateY
One trick often used with CSS-only hacks, is to duplicate markup — in this instance, the link iteself — and place it within parent wrappers that will be turned on or off by different means. The downsides to this method are that you get a rather ugly markup, and in this particular instance a bullet-number that appears jarringly (because of having to move the opacity animation from the li to the textarea).
The benefits of this method however are that by moving the link inside the li you can use -100% on the y-axis with a translate, or another offset method. Oddly though I can't work out what translateY(-100%) is calculating based upon... it doesn't seem to be the parent height, perhaps it is the height of itself. For this reason I've updated the fiddle to use bottom and relative positioning instead, although in Firefox (on mac) this glitches briefly.
It does seem to be that translateY is calculating percentage based on it's own height, so in order to get around this problem I've had to make use of position absolute and force the the link layer to assume the same dimensions as the li... annoying, as it involves z-indexing the textarea above the link, and an internal span to offset the link text, but at least it does work.
The following code works in the latest Firefox, and would work in other modern browsers if all the different browser-prefixes were correctly used to define the animation keyframes, I don't have time to set them all up right now however.
markup:
<ol class="list">
<li><textarea></textarea><a class="add" href="#"><span>Add another</span></a></li>
<li><textarea></textarea><a class="add" href="#"><span>Add another</span></a></li>
</ol>
css:
ol li {
position: relative;
}
ol li .add {
display: none;
}
ol li:last-child .add {
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
width: 100%;
height: 100%;
display: block;
animation-duration: 1s;
animation-name: slide;
}
ol li:last-child .add span {
position: absolute;
bottom: -20px;
}
.list li textarea {
position: relative;
animation-duration: 1s;
animation-name: append;
z-index: 1;
}
#keyframes append {
from {
transform: translateX(10%);
opacity: 0;
}
to {
transform: none;
opacity: 1;
}
}
#keyframes slide {
from {
transform: translateY(-100%);
}
to {
transform: none;
}
}
javascript version (code triggered translations)
http://jsfiddle.net/7m8F9/1/
The following obviously doesn't take into account the fact that you are using a template engine to power your DOM manipulations, but all the code needs to work properly is a before and after height of the list (to calculate the difference in height), and an event to trigger at the point where the new list item is added.
Sadly it is not yet possible to do this all in pure CSS, at least not as far as I have seen, perhaps once calc has leveled up...? Or perhaps if some way is introduced to reference the current elements dimensions, not just it's offset parent.
It should be noted I didn't have Internet Explorer around to test this with, but all other modern browsers seem happy.
markup:
<ol class="list">
<li><textarea></textarea></li>
<li><textarea></textarea></li>
</ol>
<div class="add">
Add another
</div>
javascript (with jQuery):
function prefix(){
for ( var a = ['Webkit','Moz','O','ms'], i=0, l = a.length; i<l; i++ ) {
if ( document.body.style[a[i]+'AnimationName'] !== undefined ) {
return { js: a[i], css: '-' + a[i].toLowerCase() + '-' };
}
}
return { css:'', js:'' };
}
$(function(){
$('.add a').click(function(e){
e.preventDefault();
var pref = prefix(),
link = $(this).parent(),
list = $('.list'),
lihi = list.height(),
liad = $('<li><textarea></textarea></li>').appendTo(list),
lihd = lihi - list.height();
link.css(pref.css + 'transform', 'translateY(' + lihd + 'px)');
setTimeout(function(){link.addClass('translate-zero transition-all');},0);
setTimeout(function(){
link.css(pref.css + 'transform', '');
link.removeClass('translate-zero transition-all');
},500);
});
});
css:
.transition-all {
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
-ms-transition: all 0.5s;
-o-transition: all 0.5s;
transition: all 0.5s;
}
.translate-zero {
-webkit-transform: translateY(0) !important;
-moz-transform: translateY(0) !important;
-ms-transform: translateY(0) !important;
-o-transform: translateY(0) !important;
transform: translateY(0) !important;
}
.list li {
animation-duration: 1s;
animation-name: append;
}
#keyframes append {
from {
transform: translateX(10%);
opacity: 0;
}
to {
transform: none;
opacity: 1;
}
}
redesign version
A number of times I have hit a similar issue, only to find a redesign helps do away with the problem and can often actually improve usability. In your case it may be best to place the "add link" above the list (or top right), or integrate the button as a floating icon somewhere... where-ever you put it, it is best to try and keep it in a static location, moving interaction points can be annoying for users, especially if they wish to add more than one item in quick succession.
The simplest solution that i could think of is this.
When you add a new li element, just append it in the dom.
liMarkup = '<li><textarea></textarea></li>'
$('ol').append(liMarkup);
$('ol').find('li').last().css('display','none');
$('ol').find('li').last().show('fast');
This would work as per your requirement :) I hope it helps.
Working Jsfiddle
EDIT: Its easy and better to do it in JS.

180 degree rotated div is only clickable from one side

I've run into a rather strange problem. I have a div that is rotatable via CSS3. The div has a front div child and back div child, with the back div having -webkit-transform: rotateY( 180deg ) set.
The problem that once the parent element is rotated to display the back side of the div, it will only detect clicks of child elements on exactly one side of the div, specifically the second half of the div or right side. The front side div detects clicks on the entire face of element. Also, the z-indexes are fine. I assume that the issue may be due to the rotation and the browser displaying one half of the side "closer"?
The code that this is breaking is extremely complex, so I created a test file to demonstrate the problem below. I'm using a jQuery plugin I wrote for the 3D transformations, which can be found here https://github.com/pwhisenhunt/jquery.transform/blob/master/jquery.transform.js.
Edit: After experimentation, the clicking of the button element is only registering from 100-200px and not from 0-100px. In other words, it is in fact only registering on the second half of the div.
Any help is very much appreciated!
<html>
<head>
<style>
.element{
width:200;
height:200;
-webkit-perspective: 800;
-webkit-transform-style: preserve-3d;
}
.element figure {
display: block;
height: 100%;
width: 100%;
position: absolute;
-webkit-backface-visibility: hidden;
border:1px solid yellow;
}
.element .front {
-webkit-border-radius:8px;
padding: 0px;
margin: 0px;
background-color:yellow;
z-index: 9870;
}
.element .back {
-webkit-border-radius:8px;
padding: 0px;
margin: 0;
-webkit-transform: rotateY( 180deg );
z-index: 0;
border: 1px solid red;
background-color:green;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script src="https://raw.github.com/pwhisenhunt/jquery.transform/master/jquery.transform.js"></script>
<script>
$(function(){
var temp = false;
$(".element").click(function(){
if(temp == false){
$(this).transform("setAnimationDuration", 1).transform("rotateY", 180);
$(this).unbind("mouseenter mouseleave");
button = $(document.createElement('div')).attr("id", "button").css({ width: 200, height: 50, backgroundColor:"blue" });
button.click(function(){
console.log("Clicking");
});
temp = true;
$(this).append(button);
}
})
})
</script>
</head>
<body>
<div class="element">
<figure class="front"></front>
<figure class="back"></front>
</div>
</body>
</html>
A JSFiddle Example of the Problem - Can be found HERE!
I know this reply is a bit too late for most of us here, but I ran into this problem earlier today, and none of the replies helped me solve it.
Solution by #kristiankeane made the other half non-clickable. I was using a container for the wrapper as well. Turns out, it's an odd bug in webkit, and I was able to fix it and make 100% of the element clickable by changing transform: rotateY(180deg) to transform: rotateY(-180deg)
It's really odd, and I don't know how it worked, but it did. I hope this helps someone!
I had this exact same issue, was able to fix it by slightly changing the parent rotation when flipped - I changed
`.flip-holder.flipped {
-webkit-transform: rotateY(180deg);
-moz-transform: rotateY(180deg);
transform: rotateY(180deg);
}`
to
`$.flip-holder.flipped {
-webkit-transform: rotateY(180.5deg);
-moz-transform: rotateY(180.5deg);
transform: rotateY(180.5deg);
}`
and the entire backface (plus overflowed elements positioned absolutely) were now clickable, and the browser did not render the extra 0.5deg of rotation so text and images are clear.
Translate both front and back just a little bit and they won't overlap.
Example:
.element .front {
-webkit-transform: translateZ(1px);
}
.element .back {
-webkit-transform: rotateY(180deg) translateZ(1px);
}
it seems that you are missing a container (in much the same way I was missing it).
see the official documentation
it's not the outer element being flipped, but a wrapper inside it. that in turn causes one of two divs to be displayed (and the transition to occur)
If your flip card structure is like this:
<div class="flipcard">
<div class="flipcard-front">
</div>
<div class="flipcard-back">
</div>
</div>
then add this to your CSS:
.flipcard:hover {
pointer-events: none;
}
.flipcard-back:hover {
pointer-events: auto;
}
could It be (and I'm just speculating) that you should use a live or delegate event bind instead of the regular. I'm speculating the click event maybe 'remembers' some how the original div position without the rotating.
After all tricks as rotate to back and rotate to 180.5 and other...
problem fixed only the following way:
When the rotation ends - create new element, clone html from rotated element, and insert new element instead of old