I'm trying to develop a website from scratch. I'm using html, css and js (with jQuery and other js libraries as I need them). I wanted to have code that could be dropped anywhere without needing to install extra plugins/script. All that should be needed should be in a single folder I can place anywhere and run (plug and play style) so I have all libraries (only bootstrap, jQuery and Vague.js atm) downloaded.
In one of the pages I have multiple images moving in a carousel like way. The images cover the page and are the only thing (besides the logo and menu buttons) on the page. What I need to do is have the screen/images blurred and only a circle around the cursor be visible. Currently I managed to blur all images and have them focused when the mouse is over them but I can't figure out how to have only a small portion of the image focused.
I know that this is not the ideal way of handling things but to get the carousel working smoothly and be replicable in multiple pages I had to duplicate all images and have a css transform operation with keyframes moving -50%. This part I know could be better and you can ignore. What I currently have is:
<div class="scrollWrapper">
<div id="scroll" class="imgScroll" onclick="toggleAnimation();">
<img class="scrollingImage" src="image1.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image2.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image3.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image4.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image5.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image1.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image2.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image3.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image4.jpg" alt="Window showcase image">
<img class="scrollingImage" src="image5.jpg" alt="Window showcase image">
</div>
</div>
.scrollWrapper{
overflow: hidden;
}
.imgScroll{
background-color: dimgrey;
width: max-content;
font-size: 0;
z-index: -999;
height: 100vh;
}
.scrollingImage{
filter: blur(10px);
-webkit-filter: blur(10px);
display: inline-block;
height: 100%;
}
.scrollingImage:hover{
filter: blur(0px);
-webkit-filter: blur(0px);
}
#keyframes scrolling{
0% {transform: translateX(0%);}
100% {transform: translateX(-50%);}
}
.imgScroll{
animation: scrolling 60s linear infinite;
z-index: -1000;
}
body .scrollWrapper .pause{
animation-play-state: paused;
-webkit-animation-play-state: paused;
-moz-animation-play-state:paused;
-o-animation-play-state:paused;
}
function toggleAnimation() {
scroll = document.getElementById('scroll');
if(scroll.classList.contains('pause')){
scroll.classList.remove('pause');
}else{
scroll.classList.add('pause');
}
}
My current idea is to blur the div with the class scrollWrapper and somehow have an area around the cursor being focused but I'm not sure how to do it.
I've looked around and found this post but the solutions work with a single static image as far as I can tell and not with multiple moving ones. I'm now messing around with Vague.js but can't figure out how to do it.
I'm not a web developer and have worked very little with js/jquery so I'm starting to feel stupid for not being able to figure this out... This is my last resort before changing to something completely different so any help will be much appreciated.
Doing this with the new CSS backdrop-filter is quite easy, the hardest part being to set up a hole in the filter.
Luckily we have stackoverflow.
For a circle, the simplest is probably to use a radial-gradient as a mask-image, as shown in this answer.
const blur_elem = document.getElementById( "blur-around" );
document.onmousemove = (evt) => {
blur_elem.style.transform = `translate(${evt.clientX}px, ${evt.clientY}px)`;
};
#blur-around {
position: fixed;
z-index: 999;
pointer-events: none;
/* twice the viewport size so it always covers fully */
width: 200vw;
height: 200vh;
/* negative offset by half so we are sure we cover the full viewport */
left: -100vw;
top: -100vh;
/* we'll use transform translate to move it */
transform-origin: center;
-webkit-backdrop-filter: blur(15px);
backdrop-filter: blur(15px);
-webkit-mask-image: radial-gradient(50px at 50% 50%, transparent 100%, black 100%);
mask-image: radial-gradient(50px at 50% 50% , transparent 100%, black 100%)
}
/* falback for browsers that don't have backdrop-filter */
#supports not ((backdrop-filter: blur(0px)) or (-webkit-backdrop-filter: blur(0px))) {
#blur-around {
background-color: rgba(255,255,255,.8);
}
}
<div id="blur-around"></div>
<p>Works over any content</p>
<img src="https://picsum.photos/250/250">
<img src="https://picsum.photos/360/200">
Unfortunately, Safari doesn't support entirely the mask-image property, so we may need something else.
We can also use CSS clip-path with an evenodd path, as shown in that answer.
Unfortunately, Chrome still doesn't support the path() function for clip-path, so we have to be creative and instead use the polygon() function and define each point's vertex.
Still, for a rectangular shape all it requires is to draw first the outer rectangle the size of our element, and then the inner one wherever we want, while ensuring we always close both of these shapes.
const blur_elem = document.getElementById( "blur-around" );
document.onmousemove = (evt) => {
blur_elem.style.transform = `translate(${evt.clientX}px, ${evt.clientY}px)`;
};
#blur-around {
position: fixed;
z-index: 999;
pointer-events: none;
/* twice the viewport size so it always covers fully */
width: 200vw;
height: 200vh;
/* negative offset by half so we are sure we cover the full viewport */
left: -100vw;
top: -100vh;
/* we'll use transform translate to move it */
transform-origin: center;
-webkit-backdrop-filter: blur(15px);
backdrop-filter: blur(15px);
--rect-size: 100px;
clip-path: polygon( evenodd,
/* outer rect */
0 0, /* top - left */
100% 0, /* top - right */
100% 100%, /* bottom - right */
0% 100%, /* bottom - left */
0 0, /* and top - left again */
/* do the same with inner rect */
calc(50% - var(--rect-size) / 2) calc(50% - var(--rect-size) / 2),
calc(50% + var(--rect-size) / 2) calc(50% - var(--rect-size) / 2),
calc(50% + var(--rect-size) / 2) calc(50% + var(--rect-size) / 2),
calc(50% - var(--rect-size) / 2) calc(50% + var(--rect-size) / 2),
calc(50% - var(--rect-size) / 2) calc(50% - var(--rect-size) / 2)
);
}
/* falback for browsers that don't have backdrop-filter */
#supports not ((backdrop-filter: blur(0px)) or (-webkit-backdrop-filter: blur(0px))) {
#blur-around {
background-color: rgba(255,255,255,.8);
}
}
<div id="blur-around"></div>
<p>Works over any content</p>
<img src="https://picsum.photos/250/250">
<img src="https://picsum.photos/360/200">
To make it with a circle though (as has been required in comments) starts to be a bit less readable and while it can be hardcoded in CSS too, it would make for such a big rule that I prefer to leave directly a javascript generator in this answer:
function makeCircleHoleClipPathRule( radius ) {
const inner_path = [];
const circumference = Math.PI * radius;
const step = Math.PI * 2 / circumference;
// we are coming from top-left corner
const start_step = circumference * (5 / 8);
for( let i = start_step; i < circumference + start_step; i++ ) {
const angle = step * i;
const x = radius * Math.cos( angle );
const y = radius * Math.sin( angle );
const str = `calc( 50% + ${ x }px ) calc( 50% + ${ y }px )`;
inner_path.push( str );
}
// avoid rounding issues
inner_path.push( inner_path[ 0 ] );
return `polygon( evenodd,
/* outer rect */
0 0, /* top - left */
100% 0, /* top - right */
100% 100%, /* bottom - right */
0% 100%, /* bottom - left */
0 0, /* and top - left again */
${ inner_path.join( "," ) }
)`;
}
const blur_elem = document.getElementById( "blur-around" );
// set the clip-path rule
blur_elem.style.clipPath = makeCircleHoleClipPathRule( 50 );
document.onmousemove = (evt) => {
blur_elem.style.transform = `translate(${evt.clientX}px, ${evt.clientY}px)`;
};
#blur-around {
position: fixed;
z-index: 999;
pointer-events: none;
/* twice the viewport size so it always covers fully */
width: 200vw;
height: 200vh;
/* negative offset by half so we are sure we cover the full viewport */
left: -100vw;
top: -100vh;
/* we'll use transform translate to move it */
transform-origin: center;
-webkit-backdrop-filter: blur(15px);
backdrop-filter: blur(15px);
}
/* falback for browsers that dont have backdrop-filter */
#supports not ((backdrop-filter: blur(0px)) or (-webkit-backdrop-filter: blur(0px))) {
#blur-around {
background-color: rgba(255,255,255,.8);
}
}
<div id="blur-around"></div>
<p>Works over any content</p>
<img src="https://picsum.photos/250/250">
<img src="https://picsum.photos/360/200">
However, backdrop-filter is currently supported only in latest Blink + Webkit browsers, Gecko is still lacking support for it. Since I doubt there are many other cross-browser solutions, you could try this polyfill which will duplicate your page's content in an iframe (i.e not very performant).
Related
How can I position several <img> elements into a circle around another and have those elements all be clickable links as well? I want it to look like the picture below, but I have no idea how to achieve that effect.
Is this even possible?
2020 solution
Here's a more modern solution I use these days.
I start off by generating the HTML starting from an array of images. Whether the HTML is generated using PHP, JS, some HTML preprocessor, whatever... this matters less as the basic idea behind is the same.
Here's the Pug code that would do this:
//- start with an array of images, described by url and alt text
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- } /* and so on, add more images here */
- ];
- let n_imgs = imgs.length;
- let has_mid = 1; /* 0 if there's no item in the middle, 1 otherwise */
- let m = n_imgs - has_mid; /* how many are ON the circle */
- let tan = Math.tan(Math.PI/m); /* tangent of half the base angle */
.container(style=`--m: ${m}; --tan: ${+tan.toFixed(2)}`)
- for(let i = 0; i < n_imgs; i++)
a(href='#' style=i - has_mid >= 0 ? `--i: ${i}` : null)
img(src=imgs[i].src alt=imgs[i].alt)
The generated HTML looks as follows (and yes, you can write the HTML manually too, but it's going to be a pain to make changes afterwards):
<div class="container" style="--m: 8; --tan: 0.41">
<a href='#'>
<img src="image_mid.jpg" alt="alt text"/>
</a>
<a style="--i: 1">
<img src="first_img_on_circle.jpg" alt="alt text"/>
</a>
<!-- the rest of those placed on the circle -->
</div>
In the CSS, we decide on a size for the images, let's say 8em. The --m items are positioned on a circle and it's if they're in the middle of the edges of a polygon of --m edges, all of which are tangent to the circle.
If you have a hard time picturing that, you can play with this interactive demo which constructs the incircle and circumcircle for various polygons whose number of edges you pick by dragging the slider.
This tells us that the size of the container must be twice the radius of the circle plus twice half the size of the images.
We don't yet know the radius, but we can compute it if we know the number of edges (and therefore the tangent of half the base angle, precomputed and set as a custom property --tan) and the polygon edge. We probably want the polygon edge to be a least the size of the images, but how much we leave on the sides is arbitrary. Let's say we have half the image size on each side, so the polygon edge is twice the image size. This gives us the following CSS:
.container {
--d: 6.5em; /* image size */
--rel: 1; /* how much extra space we want between images, 1 = one image size */
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
--s: calc(2*var(--r) + var(--d)); /* container size */
position: relative;
width: var(--s); height: var(--s);
background: silver /* to show images perfectly fit in container */
}
.container a {
position: absolute;
top: 50%; left: 50%;
margin: calc(-.5*var(--d));
width: var(--d); height: var(--d);
--az: calc(var(--i)*1turn/var(--m));
transform:
rotate(var(--az))
translate(var(--r))
rotate(calc(-1*var(--az)))
}
img { max-width: 100% }
See the old solution for an explanation of how the transform chain works.
This way, adding or removing an image from the array of images automatically arranges the new number of images on a circle such that they're equally spaced out and also adjusts the size of the container. You can test this in this demo.
OLD solution (preserved for historical reasons)
Yes, it is very much possible and very simple using just CSS. You just need to have clear in mind the angles at which you want the links with the images (I've added a piece of code at the end just for showing the angles whenever you hover one of them).
You first need a wrapper. I set its diameter to be 24em (width: 24em; height: 24em; does that), you can set it to whatever you want. You give it position: relative;.
You then position your links with the images in the center of that wrapper, both horizontally and vertically. You do that by setting position: absolute; and then top: 50%; left: 50%; and margin: -2em; (where 2em is half the width of the link with the image, which I've set to be 4em - again, you can change it to whatever you wish, but don't forget to change the margin in that case).
You then decide on the angles at which you want to have your links with the images and you add a class deg{desired_angle} (for example deg0 or deg45 or whatever). Then for each such class you apply chained CSS transforms, like this:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
where you replace {desired_angle} with 0, 45, and so on...
The first rotate transform rotates the object and its axes, the translate transform translates the object along the rotated X axis and the second rotate transform brings back the object into position.
The advantage of this method is that it is flexible. You can add new images at different angles without altering the current structure.
CODE SNIPPET
.circle-container {
position: relative;
width: 24em;
height: 24em;
padding: 2.8em;
/*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
border: dashed 1px;
border-radius: 50%;
margin: 1.75em auto 0;
}
.circle-container a {
display: block;
position: absolute;
top: 50%; left: 50%;
width: 4em; height: 4em;
margin: -2em;
}
.circle-container img { display: block; width: 100%; }
.deg0 { transform: translate(12em); } /* 12em = half the width of the wrapper */
.deg45 { transform: rotate(45deg) translate(12em) rotate(-45deg); }
.deg135 { transform: rotate(135deg) translate(12em) rotate(-135deg); }
.deg180 { transform: translate(-12em); }
.deg225 { transform: rotate(225deg) translate(12em) rotate(-225deg); }
.deg315 { transform: rotate(315deg) translate(12em) rotate(-315deg); }
<div class='circle-container'>
<a href='#' class='center'><img src='image.jpg'></a>
<a href='#' class='deg0'><img src='image.jpg'></a>
<a href='#' class='deg45'><img src='image.jpg'></a>
<a href='#' class='deg135'><img src='image.jpg'></a>
<a href='#' class='deg180'><img src='image.jpg'></a>
<a href='#' class='deg225'><img src='image.jpg'></a>
<a href='#' class='deg315'><img src='image.jpg'></a>
</div>
Also, you could further simplify the HTML by using background images for the links instead of using img tags.
EDIT: example with fallback for IE8 and older (tested in IE8 and IE7)
Here is the easy solution without absolute positioning:
.container .row {
margin: 20px;
text-align: center;
}
.container .row img {
margin: 0 20px;
}
<div class="container">
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
</div>
http://jsfiddle.net/mD6H6/
Using the solution proposed by #Ana:
transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)
I created the following jsFiddle that places circles dynamically using plain JavaScript (jQuery version also available).
The way it works is rather simple:
document.querySelectorAll( '.ciclegraph' ).forEach( ( ciclegraph )=>{
let circles = ciclegraph.querySelectorAll( '.circle' )
let angle = 360-90, dangle = 360 / circles.length
for( let i = 0; i < circles.length; ++i ){
let circle = circles[i]
angle += dangle
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth / 2}px) rotate(-${angle}deg)`
}
})
.ciclegraph {
position: relative;
width: 500px;
height: 500px;
margin: calc(100px / 2 + 0px);
}
.ciclegraph:before {
content: "";
position: absolute;
top: 0; left: 0;
border: 2px solid teal;
width: calc( 100% - 2px * 2);
height: calc( 100% - 2px * 2 );
border-radius: 50%;
}
.ciclegraph .circle {
position: absolute;
top: 50%; left: 50%;
width: 100px;
height: 100px;
margin: calc( -100px / 2 );
background: teal;
border-radius: 50%;
}
<div class="ciclegraph">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
Building off #Ana's excellent answer, I created this dynamic version that allows you to add and remove elements from the DOM and maintain proportionate spacing between the elements - check out my fiddle: https://jsfiddle.net/skwidbreth/q59s90oy/
var list = $("#list");
var updateLayout = function(listItems) {
for (var i = 0; i < listItems.length; i++) {
var offsetAngle = 360 / listItems.length;
var rotateAngle = offsetAngle * i;
$(listItems[i]).css("transform", "rotate(" + rotateAngle + "deg) translate(0, -200px) rotate(-" + rotateAngle + "deg)")
};
};
$(document).on("click", "#add-item", function() {
var listItem = $("<li class='list-item'>Things go here<button class='remove-item'>Remove</button></li>");
list.append(listItem);
var listItems = $(".list-item");
updateLayout(listItems);
});
$(document).on("click", ".remove-item", function() {
$(this).parent().remove();
var listItems = $(".list-item");
updateLayout(listItems);
});
#list {
background-color: blue;
height: 400px;
width: 400px;
border-radius: 50%;
position: relative;
}
.list-item {
list-style: none;
background-color: red;
height: 50px;
width: 50px;
position: absolute;
top: 50%;
left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<ul id="list"></ul>
<button id="add-item">Add item</button>
Here is a version I made in React from the examples here.
CodeSandbox Example
import React, { useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const graph = useRef(null);
useEffect(() => {
const ciclegraph = graph.current;
const circleElements = ciclegraph.childNodes;
let angle = 360 - 90;
let dangle = 360 / circleElements.length;
for (let i = 0; i < circleElements.length; i++) {
let circle = circleElements[i];
angle += dangle;
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth /
2}px) rotate(-${angle}deg)`;
}
}, []);
return (
<div className="App">
<div className="ciclegraph" ref={graph}>
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
</div>
</div>
);
}
You can certainly do it with pure css or use JavaScript. My suggestion:
If you already know that the images number will never change just calculate your styles and go with plain css (pros: better performances, very reliable)
If the number can vary either dynamically in your app or just may vary in the future go with a Js solution (pros: more future-proof)
I had a similar job to do, so I created a script and open sourced it here on Github for anyone who might need it. It just accepts some configuration values and simply outputs the CSS code you need.
If you want to go for the Js solution here's a simple pointer that can be useful to you. Using this html as a starting point being #box the container and .dot the image/div in the middle you want all your other images around:
Starting html:
<div id="box">
<div class="dot"></div>
<img src="my-img.jpg">
<!-- all the other images you need-->
</div>
Starting Css:
#box{
width: 400px;
height: 400px;
position: relative;
border-radius: 100%;
border: 1px solid teal;
}
.dot{
position: absolute;
border-radius: 100%;
width: 40px;
height: 40px;
left: 50%;
top: 50%;
margin-left: -20px;
margin-top: -20px;
background: rebeccapurple;
}
img{
width: 40px;
height: 40px;
position: absolute;
}
You can create a quick function along these lines:
var circle = document.getElementById('box'),
imgs = document.getElementsByTagName('img'),
total = imgs.length,
coords = {},
diam, radius1, radius2, imgW;
// get circle diameter
// getBoundingClientRect outputs the actual px AFTER transform
// using getComputedStyle does the job as we want
diam = parseInt( window.getComputedStyle(circle).getPropertyValue('width') ),
radius = diam/2,
imgW = imgs[0].getBoundingClientRect().width,
// get the dimensions of the inner circle we want the images to align to
radius2 = radius - imgW
var i,
alpha = Math.PI / 2,
len = imgs.length,
corner = 2 * Math.PI / total;
// loop over the images and assign the correct css props
for ( i = 0 ; i < total; i++ ){
imgs[i].style.left = parseInt( ( radius - imgW / 2 ) + ( radius2 * Math.cos( alpha ) ) ) + 'px'
imgs[i].style.top = parseInt( ( radius - imgW / 2 ) - ( radius2 * Math.sin( alpha ) ) ) + 'px'
alpha = alpha - corner;
}
You can see a live example here
There is no way to magically place clickable items in a circle around another element with CSS.
The way how I would do this is by using a container with position:relative;. And then place all the elements with position:absolute; and using top and left to target it's place.
Even though you haven't placed jquery in your tags it might be best to use jQuery / javascript for this.
First step is placing your center image perfectly in the center of the container using position:relative;.
#centerImage {
position:absolute;
top:50%;
left:50%;
width:200px;
height:200px;
margin: -100px 0 0 -100px;
}
After that you can place the other elements around it by using an offset() of the centerImage minus the offset() of the container. Giving you the exact top and left of the image.
var left = $('#centerImage').offset().left - $('#centerImage').parent().offset().left;
var top = $('#centerImage').offset().top - $('#centerImage').parent().offset().top;
$('#surroundingElement1').css({
'left': left - 50,
'top': top - 50
});
$('#surroundingElement2').css({
'left': left - 50,
'top': top
});
$('#surroundingElement3').css({
'left': left - 50,
'top': top + 50
});
What I've done here is placing the elements relative to the centerImage. Hope this helps.
You could do it like this: fiddle
Don't mind the positioning, its a quick example
The first step is to have 6 long columnar boxes:
The second step is to use position: absolute and move them all into the middle of your container:
And now rotate them around the pivot point located at the bottom center. Use :nth-child to vary rotation angles:
div {
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
rotate: (360deg / 6) * $n;
}
}
Now all you have to do is to locate your images at the far end of every column, and compensate the rotation with an anti-rotation :)
Full source:
<div class="flower">
<div class="petal">1</div>
<div class="petal">2</div>
<div class="petal">3</div>
<div class="petal">4</div>
<div class="petal">5</div>
<div class="petal">6</div>
</div>
.flower {
width: 300px;
height: 300px;
// We need a relative position
// so that children can have "position:abolute"
position: relative;
.petal {
// Make sure petals are visible
border: 1px solid #999;
// Position them all in one point
position: absolute; top: 0; left: 50%;
display: inline-block;
width: 30px; height: 150px;
// Rotation
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
// Petal rotation
$angle: (360deg / 6) * $n;
rotate: $angle;
// Icon anti-rotation
.icon { rotate: -$angle; }
}
}
}
}
See CodePen
I have created an application in ReactJS
HTML
<div id="root"></div>
React JS
function MouseCursor() {
return(
<div>
<img src='https://usabilla.com/graphics/resources/usabilla-logo.png' style={{cursor: "wait", backgroundRepeat: "no-repeat", height: "160px", width:"80%"}} />
<p>Right Now the cursor image changes when overing the image</p>
</div>
)
}
ReactDOM.render(<MouseCursor />, document.querySelector("#root"))
the jsfiddle for this code is :
https://jsfiddle.net/vewzyo2x/
When I hover the image the cursor changes, but I need to change the cursor of the mouse only when a certain part of the image is hovered
as shown in the below image
I need to change the cursor of the image only when the mouse is hovered on the circle shown in the above image. How can I do that?
If you measure various distances on the image when the 'blob' is circular you get CSS to calculate what dimensions and what positioning (in % terms) the blob has in relation to the whole image. As the image is stretched, the blob will stretch accordingly.
In this vanilla JS snippet the logo image is shown as the background to the div and the blob is its child div. This saves having to add another div into the DOM which wouldn't add more meaning.
The measurements were just taken with a ruler (the units don't matter)
.usabilla {
--h: 8.75;
/* image height measured by a ruler */
--w: 19.4;
/* image width */
--top: 1.5;
/* position of the blob */
--left: 11.7;
--diam: 2;
/* diameter of the blob (when the blob was circular) */
width: 80%;
height: 160px;
background-image: url(https://usabilla.com/graphics/resources/usabilla-logo.png);
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center center;
position: relative;
}
.blob {
cursor: wait;
height: calc(var(--diam) / var(--h) * 100%);
width: calc(var(--diam) / var(--w) * 100%);
position: absolute;
top: calc(var(--top) / var(--h) * 100%);
left: calc(var(--left) / var(--w) * 100%);
background-color: transparent;
border-radius: 50%;
}
<div class="usabilla">
<div class="blob"></div>
</div>
I am working on the mobile version of my website. I positioned one element below another (red below blue).
These elements are shaped with: clip-path: polygon(0% 0%, 100% 0%, 100% 50%, 100% 62%, 50% 85%, 0% 62%).
They also got an overlay, which got a text positioned inside. My goal is to fix this text block in the lower right corner for every display resolution.
How it should look on every device:
How it looks, when I change the viewport width:
First I wrote several #media (...) queries, to position the text blocks.
I noticed that I would have to write a query for almost every device individually, since the required top-spacing and angle of the text blocks are always changing.
So I tried to calculate the needed angle and the needed value for top. I found a method on stackoverflow that looks like that:
function calculate() {
const deviceWidth = screen.width;
const viewportWidth = window.innerWidth;
const currentRatio = viewportWidth / deviceWidth;
const angle = currentRatio * ...; // I don't know
const top = ...; // I don't know
document.querySelector('.text-container').style.transform = `rotate(${angle}deg)`;
document.querySelector('.text-container').style.top = `${top}vh`;
}
calculate();
window.addEventListener('resize', calculate);
I'm pretty sure, that this method is a helpful fundamental, but I don't know how to move on.
Thanks.
I would do this differently using gradient and mask:
.box {
padding-top: 200px; /* this will control the overal height */
overflow:hidden;
position:relative;
}
.box div {
padding: 10px 0 10px 100%; /* padding-left:100% to push the text to the center */
color: #fff;
font-size:25px;
background: #248a8a;
transform-origin:bottom;
transform:rotate(-20deg); /* control the rotation of the text */
margin:0 -50% 0; /* negative margin to create some overlow and avoid the bad effect of rotation */
}
.one {
background:cyan;
}
.one::before {
content:"";
position:absolute;
z-index:9;
pointer-events:none;
inset:0;
background:
linear-gradient(to bottom right,#0000 49.8%,#dc143c 50%) bottom 0 right calc(50% - 500px),
linear-gradient(to bottom left ,#0000 49.8%,#dc143c 50%) bottom 0 left calc(50% - 500px);
/* keep the 1000px a random but big value
adjust 363px based on the angle you will be using
The formula is tan(20deg) = 363/1000
*/
background-size:1000px 363px;
background-repeat:no-repeat;
}
.two {
background:#dc143c;
-webkit-mask:
linear-gradient(to bottom right,#000 49.8%,#0000 50%) bottom 0 right calc(50% - 500px),
linear-gradient(to bottom left ,#000 49.8%,#0000 50%) bottom 0 left calc(50% - 500px);
/* same logic as above */
-webkit-mask-size:1000px 363px;
-webkit-mask-repeat:no-repeat;
}
.two div {
background:#7c2c3c;
}
<div class="box one">
<div>Text block 1</div>
</div>
<div class="box two">
<div>Text block 1</div>
</div>
I want to be able to move an img around within its container once the image is zoomed in, because as you can see once you click the image it becomes too big and you can't see the whole image. Also how can I make the image goes back to normal once it's not being hovered? thanks in advance.
// Zoom in/out clothing img
$('.image').click(function() {
$(this).toggleClass('normal-zoom zoom-in');
});
.container {
width: 800px;
margin: 0 auto;
border: 2px solid black;
display: flex;
}
.img-wrapper {
margin: 10px;
overflow: hidden;
}
.image {
width: 100%;
height: 100%;
}
.text {
width: 40%;
padding: 20px;
}
.normal-zoom {
transform: scale(1);
cursor: zoom-in;
transition: all 250ms;
}
.zoom-in {
transform: scale(1.6);
cursor: zoom-out;
transition: all 250ms;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="img-wrapper">
<img src="https://static1.squarespace.com/static/560c458be4b0af26f729d191/560c5de0e4b083d9c365515f/560d53d4e4b03b1013fd40de/1443714010032/lauren-winter-wide-pant-natural_0178.jpg?format=750w" class="image normal-zoom">
</div>
<p class="text">Kept in sent gave feel will oh it we. Has pleasure procured men laughing shutters nay. Old insipidity motionless continuing law shy partiality. Depending acuteness dependent eat use dejection. Unpleasing astonished discovered not nor shy. Morning hearted now met yet beloved evening. Has and upon his last here must. Cottage out enabled was entered greatly prevent message. No procured unlocked an likewise. Dear but what she been over gay felt body. Six principles advantages and use entreaties decisively. Eat met has dwelling unpacked see whatever followed. Court in of leave again as am. Greater sixteen to forming colonel no on be. So an advice hardly barton. He be turned sudden engage manner spirit.</p>
</div>
Since you're using transform: scale() for the zoom effect it's faster and more correct to modify transform-origin to change the center point of the zoom effect on mousemove:
// Zoom in/out clothing img
$('.image').click(function() {
$(this).toggleClass('normal-zoom zoom-in');
});
$('.image').on('mousemove', function(event) {
// This gives you the position of the image on the page
var bbox = event.target.getBoundingClientRect();
// Then we measure how far into the image the mouse is in both x and y directions
var mouseX = event.clientX - bbox.left;
var mouseY = event.clientY - bbox.top;
// Then work out how far through the image as a percentage the mouse is
var xPercent = (mouseX / bbox.width) * 100;
var yPercent = (mouseY / bbox.height) * 100;
// Then we change the `transform-origin` css property on the image to center the zoom effect on the mouse position
//event.target.style.transformOrigin = xPercent + '% ' + yPercent + '%';
// It's a bit clearer in jQuery:
$(this).css('transform-origin', (xPercent+'% ' + yPercent+ '%') );
// We add the '%' units to make sure the string looks exactly like the css declaration it becomes.
});
// If you want it to automatically trigger on hover
$('.image').on('mouseenter', function() {
$(this).addClass('zoom-in');
$(this).removeClass('normal-zoom');
});
// and stop when not hovering
$('.image').on('mouseleave', function() {
$(this).addClass('normal-zoom');
$(this).removeClass('zoom-in');
});
.container {
width: 800px;
margin: 0 auto;
border: 2px solid black;
display: flex;
}
.img-wrapper {
margin: 10px;
overflow: hidden;
}
.image {
width: 100%;
height: 100%;
}
.text {
width: 40%;
padding: 20px;
}
.normal-zoom {
transform: scale(1);
cursor: zoom-in;
transition: transform 250ms;
}
.zoom-in {
transform: scale(1.6);
cursor: zoom-out;
transition: transform 250ms;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="img-wrapper">
<img src="https://static1.squarespace.com/static/560c458be4b0af26f729d191/560c5de0e4b083d9c365515f/560d53d4e4b03b1013fd40de/1443714010032/lauren-winter-wide-pant-natural_0178.jpg?format=750w" class="image normal-zoom">
</div>
<p class="text">Kept in sent gave feel will oh it we. Has pleasure procured men laughing shutters nay. Old insipidity motionless continuing law shy partiality. Depending acuteness dependent eat use dejection. Unpleasing astonished discovered not nor shy. Morning hearted now met yet beloved evening. Has and upon his last here must. Cottage out enabled was entered greatly prevent message. No procured unlocked an likewise. Dear but what she been over gay felt body. Six principles advantages and use entreaties decisively. Eat met has dwelling unpacked see whatever followed. Court in of leave again as am. Greater sixteen to forming colonel no on be. So an advice hardly barton. He be turned sudden engage manner spirit.</p>
</div>
You can use the mousemove event listener on the image with class .zoom-in to change the left and top CSS params. Make sure to set position:relative; on the image.
Example:
$(document).on('mousemove', '.zoom-in', function( event ) {
$(".text").text(event.pageX + ", " + event.pageY);
var positionLeft = event.pageX - $(this).width()/2;
var positionTop = event.pageY - $(this).height()/2;
$(this).css({'left': positionLeft, 'top': positionTop});
});
Here is a fiddle.
I want to use HTML to create an "opening" effect of one on top of another one.
After some research i figured out a way (see JSFiddle).
I now have the problem that the background image moves a little bit when the circle is resizing.
Can anyone help me figure out how to get the background image to stand still.
The image in the circle needs to keep same zoom level when opening.
The circle needs to be centered and the bottom half needs to be out of the window.
Circle css is this:
.circle {
width: 0px;
height: 0px;
z-index: 10;
position: absolute;
border-radius: 50%;
overflow: hidden;
margin: 0 auto;
left: 50%;
-moz-transform: translate(-50%, 50%);
-webkit-transform: translate(-50%, 50%);
bottom: 0;
-moz-transition: all 1.5s;
-webkit-transition: all 1.5s;
}
JSFiddle: http://jsfiddle.net/GmvUQ/2/
Update,
Let me explain a little more. i notice that my question is not clear enough.
I have a few screenshot for the effect i want to create:
1st frame:
2nd frame
The entire effect is already working but when the transition is in progress (The circle with the image is getting bigger or smaller) the image inside the circle moves a little bit.
This is probably because of the calculations that need to be done by Javascript / CSS positioning.
I would like some help how to let this image stand entirely still during resize transition.
Thanks!
DEMO: http://jsfiddle.net/GmvUQ/5/
Updated HTML
<div>
<div class="buttons">
<button onclick="changeboleto(0)">Click here</button>
<button onclick="changeboleto(500)">Click here</button>
<button onclick="changeboleto(1000)">Click here</button>
</div>
<div class="circle girl">
</div>
<div class="circle lamborghini">
</div>
</div>
Note that I've removed the nested </div> elements within each .circle. Instead I've added an extra class for each, which sets the background-image (and some positioning for them, if necessary).
Updated CSS
.circle {
width: 250px;
height: 250px;
z-index: 10;
position: absolute;
border-radius: 50%;
overflow: hidden;
margin: 0 auto;
background-repeat: no-repeat;
background-size: cover;
background-origin: content-box;
background-position: center center;
}
.lamborghini {
background-image: url(http://www.hdwallpapers.in/walls/2013_wheelsandmore_lamborghini_aventador-wide.jpg);
}
.girl {
background-image: url(http://www.hdwallpapers.in/walls/colorful_background_girl-normal5.4.jpg);
top: 50%;
}
.buttons {
position: relative;
z-index: 5;
}
I've moved most of the CSS in to the .circle class as it is common to both image sets. Pay special attention to the values for the background-* attributes.
Updated JQuery
function changeboleto(pix) {
circleHeight = pix;
circleWidth = pix;
$('.circle').animate({
'width' : circleWidth,
'height': circleHeight
}, 1500, 'linear');
//css('width', circleWidth).css('height', circleHeight);
changeCircleBackgroundToWindow();
}
function changeCircleBackgroundToWindow() {
windowWidth = $(window).width();
windowHeight = $(window).height();
$(".circle > div").animate({
'width' : windowWidth,
'height': windowHeight
}, 1500, 'linear');
$(".circle > div").animate({
'width' : windowWidth,
'height': windowHeight
}, 1500, 'linear');
//$(".circle-background").css("width", windowWidth).css("height", windowHeight);
//$(".circle-background2").css("width", windowWidth).css("height", windowHeight);
}
Rather than mix JQuery and CSS transitions I've lumped all the animation together in the JQuery.
I've used the animate() function and specified the easing method. The default easing is swing but I've used linear as this progresses the animation at a constant pace.
Edit
The solution above includes CSS that allows the image to scale with the animation. However you are requesting that the image stays at the same "zoom level" throughout.
To achieve this simply remove a line from the CSS, namely this one:
.circle {
...
background-size: cover;
...
}
I know this is 5 years too late, but I found this thread via a search engine and thought I'd provide my own thoughts.
This effect can also be achieved with clip-path, which is a bit more forgiving than jquery's animate (which can still result in image shakiness if you're animating certain/enough properties).
clip-path has the additional benefit of not needing javascript at all if you're doing, say, hovers rather than button clicks. It also results in a simpler HTML file.
I've made an updated version of the original question's jsfiddle, http://jsfiddle.net/GmvUQ/13/ which demonstrates doing this with clip-path. It's still using jquery to handle the button clicks, but the "magic" all happens via CSS transitions, rather than javascript animations.
JQuery:
function changeboleto(pix) {
...
$('.circle-background').css('clip-path', 'circle(' + pix/2 + 'px at 50% 100%)');
}
CSS (including original CSS from original fiddle):
.circle-background {
position: absolute;
z-index: 10;
clip-path: circle(0% at 50% 100%);
background:url(http://www.hdwallpapers.in/walls/colorful_background_girl-normal5.4.jpg);
background-size: cover;
-webkit-transition: all 1.5s;
-moz-transition: all 1.5s;
bottom: 0%;
left: 50%;
-moz-transform: translateX(-50%);
-webkit-transform: translateX(-50%);
}
What this does is simply cause the CSS to transition on the clip-path property, animating the circle expansion. Because the image itself never moves, just the boundaries between which it displays, it never shakes.
Full screen demo
JSFiddle: http://jsfiddle.net/7bP7Z/4/ (Click around to see things grow)
Okay, so now that the question has more clarification I have revisited the drawing board and have come up with a better solution.
HTML
<div class="circle">
<div class="circle-overlay"></div>
<img src="http://www.hdwallpapers.in/walls/2013_wheelsandmore_lamborghini_aventador-wide.jpg" />
</div>
<div class="circle">
<div class="circle-overlay"></div>
<img src="http://www.hdwallpapers.in/walls/colorful_background_girl-normal5.4.jpg" />
</div>
Note the changes to the structure:
A containing element
An "overlay" element
An </img>
CSS
.circle {
position: relative;
overflow: hidden;
}
.circle-overlay {
position: absolute;
left: 50%;
margin-left: -150px;
bottom: -150px;
border-radius: 50%;
width: 300px;
height: 300px;
box-shadow: 0px 0px 0px 3000px white;
}
Nice and simple CSS!
The majority of the code is used to position our .circle-overlay class. This class provides a transparent circle (using border-radius) and utilises one of my favourite new CSS features - box-shadow - to apply a solid, white "outline" of an arbitrarily large value that covers the image below it. Have a play with the colour and size (adjust the 300px value) of the box-shadow to see how this works.
JQuery
$('.circle').click(function() {
var c = $(this).children('.circle-overlay');
var w = c.width() + 100;
c.animate({
'width' : w,
'height': w,
'bottom': (w*-0.5),
'margin-left': (w*-0.5)
}, 500, 'linear');
});
Once again, keeping things nice and simple!
The above JQuery performs a very simple task. It increases the size of the circle-overlay whilst maintaining its bottom, centre positioning on every click.
This should be a very smooth animation and the image should not "judder" or "shake" as the image is not being manipulated.