I'm pretty new to CSS3 animations to I'd like to know you how to do the following one:
I have a circe:
On hover, I'd like to expand the width of the circle making it into a pill shape with a transition like in this image:
This is what I tried. The issue is that the circle transitions to an ellipse as the width expands :
.logo {
background: red;
width: 100px;
height: 100px;
border-radius: 50%;
transition: width 2s;
}
.logo:hover {
width: 300px;
}
<div class="logo"></div>
You just need to change the border-radius value to any other unit than percentage (all units listed here). For an explanation of how this works and why you need to use these units for the desired output, see Border-radius in percentage (%) vs pixels (px).
Example with px. The circle expands to a pill shape on hover :
.logo {
width: 100px;
height: 100px;
border-radius: 50px;
background: red;
transition: width 2s;
}
.logo:hover {
width: 300px;
}
<div class="logo"></div>
If you don't know the height of the circle, you can set a very high value so the circle keeps its pill shape as the width expands :
.logo {
width: 200px;
height: 200px;
border-radius: 999px;
background: red;
transition: width 2s;
}
.logo:hover {
width: 400px;
}
<div class="logo"></div>
Related
I have a DIV with "width: 100%", and after some time it moves to a different width (for example "width: 50%") with a nice transition. I want that the part which is going to decrease to get a different color. How do I do it?
.container {
width: 80%;
height: 50px;
}
.bar {
background-color: red;
width: 100%;
height: 100%;
transition-duration: 1s;
}
<div class="container">
<div class="bar"></div>
</div>
You can simply put another bar exactly behind this bar. Set it's z-index to 1. And your current bar's z-index to a higher number.
The bar that is in the back, has a black color like you wanted.
Now when the red bar decrease it's width, the one in the back becomes visible.
Here you go, the most basic working example.
(Try hovering the bar and see it moving)
By simply overlaying a pseudo-element over the static .bar element, with position:absolute, it is places above the bar and has dynamic width which you should change.
.container {
width: 80%;
height: 50px;
}
/* static background bar */
.bar {
--progress: 80%; /* css variable */
position: relative;
background-color: salmon;
width: 100%;
height: 100%;
}
/* This is the part which moves: */
.bar::before{
content: '';
background-color: darkred;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
width: var(--progress);
transition: 1s;
}
.bar:hover {
--progress: 20%;
}
<div class="container">
<div class="bar"></div>
</div>
This solution uses a linear-gradient where I animate the background-size. I placed a fully black gradient on the .container, and a red one on the .bar, where I put a transition: background-size 1s on the black .container.
By using javascript to change a CSS variable, I can get the bar to animate.
Added a range to show-case the result.
Do note that it's always better to specify what attribute to animate with transiation, because otherwise the browser loops through all animatable properies, which can create janky animations.
let rangeInput = document.querySelector('input[type="range"');
let containerDiv = document.getElementById("container");
if (rangeInput) {
rangeInput.addEventListener('change', (event) => {
let newValue = event.target.value;
containerDiv.style.setProperty('--bar-width', `${newValue}%`);
});
}
input[type="range"] {
width: 80%;
margin-top: 1rem;
}
#container {
--bar-width: 100%;
width: 80%;
height: 50px;
background: linear-gradient(black, black);
transition: background-size 1s;
}
/* added "div" to obtain a higher specificity, to override the default background properties. */
div#container,
div.bar
{
background-repeat: no-repeat;
background-size: var(--bar-width);
}
.bar {
background: linear-gradient(red, red);
width: 100%;
height: 100%;
}
<div id="container">
<div class="bar"></div>
</div>
<input type="range" value="100" />
Is there a way to apply a transform: scale() to an element (or something similar, although ideally it should be able to be safely animated) which would make the element appear larger, however "ignore" the element's size (obviously this isn't exactly what I mean, keep reading).
Here is an example of what I mean.
Essentially the idea is to make the 2 differently size elements appear to scale in the same way, despite being 2 different sizes. It should be noted that the elements aren't going to be fixed width and will inherit the size of their container (whose width is also indeterminate).
Update
Thanks #TemaniAfif, the solution can be optimized further by manipulating the left and right margin instead of width.
.small {
width: 100px;
margin: 10px;
background-color: #1abc9c;
}
.large {
width: 900px;
margin: 10px;
background-color: #3498db;
}
.target {
background-color: inherit;
}
.target:hover {
margin: 0 -12px;
transform: scaleY(1.2);
transition: transform 0.3s ease-in-out, margin 0.3s ease-in-out;
}
<div class="small">
<div class="target">Small</div>
</div>
<div class="large">
<div class="target">Large</div>
</div>
Original Answer
The scale function resizes the element by multiplication, but you need a constant enlargement on the variant x-axis. Instead use scale, using calc on width of a proxy element (.target) to add the constant pixel when the element needs to be enlarged. By enlarging width, the position of the element also needs an offset to make it grows to left, so set the left to a negative number which is a half of the total enlargement. Since heights of the two elements are the same, using scaleY to scale them is ok.
.small {
width: 100px;
margin: 10px;
background-color: #1abc9c;
}
.large {
width: 900px;
margin: 10px;
background-color: #3498db;
}
.target {
position: relative;
left: 0;
width: 100%;
background-color: inherit;
}
.target:hover {
left: -12px;
width: calc(100% + 24px);
transform: scaleY(1.2);
transition: transform 0.3s ease-in-out, left 0.3s ease-in-out, width 0.3s ease-in-out;
}
<div class="small">
<div class="target">Small</div>
</div>
<div class="large">
<div class="target">Large</div>
</div>
I have a parent and child div the child div has a background image. On hover the image scales and there is a transition on the transform. This is working correctly in all browsers except Safari, it seems on hover the border radius is being removed and then added again. Can anyone suggest a fix for this.
HTML:
<div class="image-box">
<div class="image"></div>
</div>
CSS:
.image-box {
width: 300px;
overflow: hidden;
border-radius: 20px;
}
.image {
width: 300px;
height: 200px;
background: url("http://via.placeholder.com/340x282");
background-position: center;
transition: transform 1s ease;
}
.image:hover {
transform: scale(1.5);
}
https://codepen.io/liannaryan/pen/ZxbZZa
For anyone having the same issue the solution is.
.image-box {
width: 300px;
overflow: hidden;
border-radius: 20px;
-webkit-border-radius: $border-radius;
-webkit-mask-image: -webkit-radial-gradient(circle, white, white);
}
This is what i am trying to achive
i have :
#image1 {
position: absolute;
bottom: 0px;
align-self: auto;
background-color: #dc022e;
width: 340px;
height: 100px;
border-radius: 50% / 100%;
border-bottom-left-radius: 0;
/*transform: rotate(10deg);*/
border-bottom-right-radius: 0;
opacity: 0.8;
}
#image2 img {
width: 80%;
}
<div>
<div id="image2">
<img src="http://t1.gstatic.com/images?q=tbn:ANd9GcThtVuIQ7CBYssbdwtzZjVLI_uw09SeLmyrxaRQEngnQAked5ZB">
</div>
<div id="image1"></div>
</div>
Finally I don't know how to make it rotated and with the margins cut like in the picture
A Quick example of this would use a pseudo element and have the image set in the background.
div {
position: relative;
height: 300px;
width: 500px;
background: url(http://lorempixel.com/500/300);/*image path*/
overflow: hidden;/*hides the rest of the circle*/
}
div:before {
content: "";
position: absolute; /*positions with reference to div*/
top: 100%;
left: 50%;
width: 0;/*define value if you didn't want hover*/
height: 0;
border-radius: 50%;
background: tomato;/*could be rgba value (you can remove opacity then)*/
opacity: 0.5;
transform: translate(-50%, -50%);/*ensures it is in center of image*/
transition: all 0.4s;
}
/*Demo Only*/
div:hover:before {/*place this in your pseudo declaration to remove the hover*/
height: 100%;
width: 150%;/*this makes the shape wider than square*/
transform: translate(-50%, -50%) rotate(5deg);/*ensures it is in center of image + rotates*/
}
div {/*This stuff is for the text*/
font-size: 40px;
line-height: 300px;
text-align: center;
}
<div>HOVER ME</div>
Instead of nested elements, you can just use a pseudo element. This is placed at the bottom of the container div. For this to work, you need position:relative and overflow:hidden on the container div. Also, pseudo elements always need the content declaration.
To modify the border radius, you just play around with left | width | height of the pseudo element. You don't need any rotation.
Instead of hex color and opacity you can as well use the "new" color space rgba(r,g,b,a) where a is the opacity value.
For the passepartout you simply use the border declaration.
#image2{
position:relative;
border:10px solid #888;
overflow:hidden;
box-shadow:0 0 4px #aaa;
}
#image2::after {
content:"";
display:block;
position: absolute;
bottom: 0;left:-10%;
background-color: #dc022e;
width: 120%;
height: 60%;
border-radius: 100% 100% 0 0;
opacity: 0.8;
}
#image2 img {
width: 100%;
display:block;
position:relative;
}
<div id="image2">
<img src="http://t1.gstatic.com/images?q=tbn:ANd9GcThtVuIQ7CBYssbdwtzZjVLI_uw09SeLmyrxaRQEngnQAked5ZB">
</div>
You can just use position: absolute for your image and position: relative for your overlay, adjusting the top position and width according to your needs. Here's a Fiddle. Hope this helps!
Edit: Here's an updated version of the Fiddle demonstrating border and overflow properties on the img container. As CBroe mentioned, rotating a circle is probably not a good use of your time in this case. Also, I definitely agree that using a pseudo element is a much cleaner approach than nesting images.
I've tried the perspective solution here How to transform each side of a shape separately? but can't get it to work probably due to the irregularness of the shape. Only the top and right side columns are slanted, vertical and bottom are straight. How can I do this with CSS?
Using CSS borders you can create triangles and trapezoids.
You can achieve your shape joining a triangle and a trapezoid.
.triangle {
border: 0 solid red;
border-left-width: 500px;
border-top-width: 30px;
border-top-color: transparent;
}
.trapezoid {
border: 0 solid red;
width: 500px;
border-bottom-width: 150px;
border-right-width: 30px;
border-right-color: transparent;
}
<div class="triangle"></div>
<div class="trapezoid"></div>
Method 1: Clip path
You could make use of CSS clip-path feature to clip a rectangle into the required polygon shape.
div {
box-sizing: border-box;
height: 150px;
width: 250px;
background: red;
padding: 10px;
-webkit-clip-path: polygon(0% 0%, 90% 10%, 100% 100%, 0% 100%);
clip-path: polygon(0% 0%, 90% 10%, 100% 100%, 0% 100%);
}
div#image {
background: url(http://lorempixel.com/400/200);
}
/* Just for demo */
div{
float: left;
margin: 10px;
transition: all 1s;
}
div:hover{
height: 200px;
width: 300px;
}
<div>Some text</div>
<div id="image"></div>
Pros:
Supports non-solid color fills inside the shape and also allow text to be present inside.
The shape is responsive and can adapt even if the container's dimensions change.
Cons:
Poor browser support for the CSS clip-path feature. This can be overcome by using inline SVG for the clip-path like in the below snippet as this has much better browser support.
div {
box-sizing: border-box;
height: 150px;
width: 250px;
padding: 10px;
background: red;
-webkit-clip-path: url(#clip);
clip-path: url(#clip);
}
div#image {
background: url(http://lorempixel.com/400/200);
}
/* Just for demo */
div{
float: left;
margin: 10px;
transition: all 1s;
}
div:hover{
height: 200px;
width: 300px;
}
<svg width="0" height="0">
<defs>
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<path d="M0 0, 0.9 0.1, 1 1, 0 1z" />
</clipPath>
</defs>
</svg>
<div>
Some text
</div>
<div id="image"></div>
Method 2: CSS Transforms
Generally it is better not to use transforms when there is going to be content like image or text inside the shape (or) when the shape's background is not going to be a solid color because then we would either have to (a) reverse transform the child elements separately to make them look normal or (b) use absolute positioning.
For this particular shape, having text inside the shape is not a problem even while using transforms but having non solid background colors would be.
Option 1: Using two pseudo-elements
You could use a couple of pseudo-elements with skew transforms, position one on the top and the other on the right to produce the required shape. Hover the shape in snippet to see how it is created.
div {
position: relative;
height: 150px;
width: 250px;
background: red;
margin: 40px 40px 0px 0px;
}
div:after,
div:before {
position: absolute;
content: '';
background: red;
z-index: -1;
backface-visibility: hidden;
}
div:before {
height: 12.5%;
width: 100%;
top: 0px;
left: 0px;
transform-origin: right top;
transform: skewY(3deg);
}
div:after {
height: 100%;
width: 12.5%;
right: -1px;
top: -1px;
transform-origin: right top;
transform: skewX(10deg);
}
/* Just for demo */
div{
transition: all 1s;
}
div:hover{
height: 250px;
width: 300px;
}
div:hover:after{
background: blue;
}
div:hover:before{
background: green;
}
<div>Some text</div>
Pros:
Shape can be created with a single element and can have text inside it without any trouble.
Cons:
Having gradients (or) images as background for the shape is complex because they would need reverse rotation as mentioned earlier.
Shape is not 100% scalable as dimensions of the container should change proportionately for the shape to be maintained (hover on the shape in the snippet to see what I mean). Reason is same as mentioned here.
Option 2: Using one pseudo-element
This is pretty similar to the previous option except that this uses a single pseudo-element along with a overflow: hidden on the parent.
div {
position: relative;
box-sizing: border-box;
height: 200px;
width: 300px;
padding: 10px;
overflow: hidden;
}
div:after {
position: absolute;
content: '';
top: 0px;
left: -20px;
height: 100%;
width: 100%;
background: red;
transform-origin: left bottom;
transform: skewY(5deg) skewX(7.5deg);
z-index: -1;
}
div:hover {
height: 300px;
width: 500px;
transition: all 1s;
}
<div>Some text</div>
Pros:
Shape can be created with a single element and can have text inside it without any trouble.
Shape is responsive and can adapt even if the container's dimensions change .
Cons:
Same constraint as the previous option for gradient and image backgrounds.
Not suitable if the overflow: hidden on the parent is a constraint.
A solution is:
div {
width: 300px;
height: 100px;
margin:50px;
background-color: yellow;
border: 1px solid black;
}
.thisdiv {
-ms-transform: skewX(-20deg); /* IE 9 */
-webkit-transform: skewX(-20deg); /* Safari */
transform: skewX(-20deg); /* Standard syntax */
}
<div class="thisdiv">
This is the div I will skew
</div>
This is how to skew an element, if you want to make the shape you added, try using two overlaping div's, position, scale, rotate and skew, like this:
.outer-div{
position:relative;
margin:50px;
width:200px;
height:200px;
border:2px black dashed;
}
.inner-one{
position:absolute;
left:0;
bottom:0;
width:180px;
height:180px;
background:red;
}
.inner-two{
position:absolute;
bottom:2px;
right:0px;
width:200px;
height:195px;
background:red;
transform: rotate(7deg) skew(14deg) scale(0.905); /* Standard syntax */
}
<div class="outer-div">
<div class="inner-one">
</div>
<div class="inner-two">
</div>
</div>
2 Triangle Solution for Irregular Quadrilateral in CSS
In looking at your image, I notice that the skew at the top and right are really just long, narrow triangles overlaying the rectangle.
So what I did was create triangles using CSS border properties and absolutely position them over the rectangle.
#rectangle {
width: 400px;
height: 200px;
background-color: red;
margin-top: 25px;
position: relative;
}
#triangle-down {
border-left: 30px solid red;
border-right: 0;
border-top: 200px solid transparent;
position: absolute;
right: -30px;
top: 0;
}
#triangle-left {
border-top: 0;
border-bottom: 15px solid red;
border-right: 400px solid transparent;
position: absolute;
right: 0;
top: -15px;
}
<div id="rectangle">
<div id="triangle-down"></div>
<div id="triangle-left"></div>
</div>
jsFiddle demo
You could check out the clip-path property (see below from MDN), but support is very patchy. Chrome 24+ supports with prefix and FF, but only URL values, which reference a path in an SVG. You can read more about clip-path here.
Here's a basic pen - this will only work in Chrome.
From MDN
The clip-path CSS property prevents a portion of an element from getting displayed by defining a clipping region to be displayed i.e, only a specific region of the element is displayed. The clipping region is a path specified as a URL referencing an inline or external SVG, or shape method such as circle(). The clip-path property replaces the now deprecated clip property.
developer.mozilla.org/en-US/docs/Web/CSS/clip-path (sorry, not enough rep to link)