CSS / JS - calculate angle and spacing for div - javascript

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>

Related

How to do animation on scroll in js on both ways (top/bottom)

I created a little aniamtion in JS by using bounding client. Once I scroll down until the text/content appears, I change its opacity to 1 by applying ".active" from CSS. And when I scroll up above the element again, opacity changes back to 0 (because ".active" gets taken away).
The problem is I want to make the same thing happen when I scroll up to the content element from below. Once the user goes below the content element, opacity should go to 0, then when they scroll back up (so the content element is again in view), opacity should go to 1. So it makes the animation work in both directions, something like on scrollrevealjs's front page.
document.addEventListener('scroll',()=>{
let content = document.querySelector('.text');
let contentPositiontop = content.getBoundingClientRect().top;
let screenPosition = window.innerHeight ;
if (contentPositiontop < screenPosition){
content.classList.add('active');
}
else{
content.classList.remove('active');
}
});
.text{
transform: translateX(700px) translateY(1000px);
font-family: Inter;
font-weight: 800;
font-size: 40px;
opacity: 0;
transition: all 2s ease;
position: absolute;
}
.active{
opacity: 1;
}
You just need to check the height of the bottom of the content element as well as the top.
(For the top, we needed to add in the screen height (window.innerHeight) because we were comparing its position to the bottom of the screen. We don't need this for the bottom because we are comparing its position to the top of the screen, which has a vertical position of 0.)
When both the bottom and the top are in range, we show the content element.
(If the content element's height were greater than the height of the screen for some reason, you would have to choose values between 0 and window.innerHeight to use to trigger the transition.)
document.addEventListener('scroll',() => {
const
content = document.querySelector('.text'),
top = content.getBoundingClientRect().top,
bottom = content.getBoundingClientRect().bottom;
if (top < innerHeight && bottom > 0){
content.classList.add('active');
}
else{
content.classList.remove('active');
}
});
.spacer{ height: 104vh; }
.text{ height: 100vh; background: blue; transform: translateX(20px); opacity: 0; transition: all 2s ease; }
.active{ opacity: 1; }
<div class="spacer"> </div>
<div class="text"></div>
<div class="spacer"> </div>
.

How Can I change the mouse cursor when hovering the specific part of the image in ReactJS?

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>

How to shrink a from a max-height to a min-height over the size decrease of the viewport width?

If I have a body element and I decrease screen size, it is a 1:1 ratio.
If I have an body tag at width 50%. its a 1:1 ratio. Every pixel decrease of the viewport directly effects the width of element.
If I have 2 Elements side by side. I want to have the first element (.left) start at a width of 400px, at a screen width of 1300px, but increase a total of 100px over the course of the screen increase to 1920px.The second element (.right) will fill the rest of the space and decrease at the according rate of the screen +/- the current width of .left
.right
1300px -> 1920px
400px -> 500px
.left
width:100%
I know this doesnt work but this is the code I've got so far.
.full{
background-color: lightblue;
width:100%;
height:50px;
}
.half{
width:50%;
height:50px;
background-color:grey;
}
#flex{
display:flex;
}
.right{
max-width:500px;
min-width:400px;
height:50px;
background-color:lightgreen;
width:70%;
}
.left{
height:50px;
background-color:lightpink;
width:30%;
}
<div class="full"></div>
<div class="half"></div>
<div id="flex">
<div class="left"></div>
<div class="right"></div>
</div>
I'm assuming the wording in the question is what is required, i.e. the left element is to have a min value of 400px, a max value of 500px and is to increase in size from the min starting at body width 1300px and stopping at 1920px.
left therefore needs to be set to have a min-width: 400px and max-width: 500px
We need to calculate what the width will be to increase by 100px between the given start and end body width values. The formula for this is:
s + (i / d) * (a - b)
where
s = starting width of left element (400)
i = increase in left width (100)
d = difference between start and end body widths (1620)
a = actual body width now (100%)
b = starting body width (1300)
The right element is to take up the remaining space so needs to be given flex: auto;
In this snippet the various dimensions have been set using CSS variables so as to make it easier to change them.
.full{
background-color: lightblue;
width: 100%;
height: 50px;
}
.half{
width: 50%;
height: 50px;
background-color:grey;
}
#flex{
display: flex;
}
.right{
height: 50px;
background-color: lightgreen;
flex: auto;
}
.left{
height:50px;
background-color:lightpink;
/* SET THESE 4 VARIABLES TO WHAT YOU WANT */
--startb: 1300; /* width of the body after which left starts to get bigger */
--endb: 1920; /* width of the body after which left stops getting bigger */
--startleft: 400; /* the start (hence minimum) width of the left element */
--incw: 100; /* the increase in the left's width when have reached the end body width */
/* we calculate some interim values just to make it a bit easier to see what's happening */
--startbw: calc(var(--startb) * 1px);
--endbw: calc(var(--endb) * 1px);
--incb: calc(var(--endb) - var(--startb));
--startw: calc(var(--startleft) * 1px);
width: calc(var(--startw) + calc(calc(var(--incw) / var(--incb)) * calc(100% - var(--startbw))));
min-width: var(--startw);
max-width: calc(var(--startw) + calc(var(--incw) * 1px));
}
<div class="full"></div>
<div class="half"></div>
<div id="flex">
<div class="left"></div>
<div class="right"></div>
</div>

CSS/JS blur except around the mouse

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).

1 pixel gap between 2 absolute div with 50% height, only on mobile (viewport)

I have a div that takes 100% height and split into 4 parts (topleft, topright, bottomleft, bottomright), each taking a quarter of the screen with absolute positions and sizes (width:50%, height:50%).
On desktop no problem. But on mobile, due to viewport scaling, 1 have 1 pixel gap between the top blocks and the bottom blocks.
I've tried changing the height value of each block according to height of the page manually (make the wrapper's height an even number). Didn't work.
I've now tried a different approach. The wrapper is actually 0px high and sits in the middle of the screen (height:0, top:50%). The top blocks are positioned with bottom:0 and the bottom ones with top:0.
But I still get this weird gap on iPad and mobiles.
Any idea?
https://jsfiddle.net/hywg8qmb/
body{
background-color:#fff;
}
.full-cache-item{
background-color: #0d0d0d;
opacity: 1;
position: absolute;
width: 50%;
height: 50%;
}
.full-cache-topleft{
bottom:0;
left: 0;
}
.full-cache-topright{
bottom:0;
right: 0;
}
.full-cache-bottomleft{
top:0;
left: 0;
}
.full-cache-bottomright{
top:0;
right: 0;
}
<div class="full-cache" data-rel="-7">
<div class="full-cache-item full-cache-topleft"></div>
<div class="full-cache-item full-cache-topright"></div>
<div class="full-cache-item full-cache-bottomleft"></div>
<div class="full-cache-item full-cache-bottomright"></div>
</div>

Categories

Resources