How to diagonally divide a square div into 4 clickable parts? - javascript

This is how to diagonally "divide" a square into 4 parts:
div {
width: 0;
height: 0;
border-top: 50px solid red;
border-right: 50px solid blue;
border-bottom: 50px solid green;
border-left: 50px solid yellow;
}
<div>
</div>
(result)
I want to attach an event to each colored area - but of course you can't attach an event to a border. How should I do?

Here is one way to do it. Nested DIVs. I used wrapper with a a grid to lay them in the 2x2 pattern, then translated and rotated the wrapper. Clipped using the outermost div. Each one is easy to use the onclick for, and you don't need to do extra logic. Just a lot more effort to setup.
I've also got this set up so that you can easily change the size of the squares. However, this definitely won't work in IE, but I don't think we really need to worry about that.
Working codepen
document.querySelector(".red").onclick = () => alert("red");
document.querySelector(".blue").onclick = () => alert("blue");
document.querySelector(".yellow").onclick = () => alert("yellow");
document.querySelector(".green").onclick = () => alert("green");
.clip {
--size: 200px;
height: var(--size);
width: var(--size);
overflow-x: hidden;
overflow-y: hidden;
}
.rotate {
height: calc(var(--size) * 1.5);
width: calc(var(--size) * 1.5);
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
transform: translate(-50%, -50%) translate(calc(var(--size) * 0.5), calc(var(--size) * 0.5)) rotate(45deg) ;
}
.red {
background: red;
}
.blue {
background: blue;
}
.yellow {
background: yellow;
}
.green {
background: green;
}
<div class="clip">
<div class="rotate">
<div class="red"> </div>
<div class="blue"> </div>
<div class="yellow"> </div>
<div class="green"> </div>
</div>
</div>

If you want to pursue a single div/different logic depending on location, this might help.
I just wrote this in the console of this page:
document.body.addEventListener("click",(e)=>console.log(e))
And clicked, in this order, on these portions: "yellow", "red", "blue", "green" of your image. ( I held ctrl while clicking, so that it would open the image in new tab and this page would persist)
The resulting events (4 loggeed, ofc) had a path attribute that said on which element I clicked (in this case, principal one was img), and the offsetX & offsetY relative to this element
The relation was:
{
"yellow": {offsetX: 18, offsetY: 59},
"red": {offsetX: 59, offsetY: 25},
"blue": {offsetX: 85, offsetY 46},
"green": {offsetX: 61, offsetY: 78},
}
So yeah, you could use, from the MouseEvent:
offsetX
The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
offsetY
The offsetY read-only property of the MouseEvent interface provides the offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node.
And determine where user clicked.

You can use clip path to make triangles and use some positioning to get them in the correct spots.
document.querySelector('.sq-tri').addEventListener("click", (evt) => {
console.log(evt.target.getAttribute('data-location'))
})
.sq-tri {
display: relative;
width: 5em;
height: 5em;
}
.tri {
position: absolute;
}
.tri-up,
.tri-down {
width: 5em;
height: 2.5em;
}
.tri-left,
.tri-right {
width: 2.5em;
height: 5em;
}
.tri:hover {
background-color: lime;
cursor: pointer;
}
.tri-up {
margin-top: 2.5em;
background-color: yellow;
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}
.tri-down {
background-color: red;
clip-path: polygon(50% 100%, 0% 0%, 100% 0%);
}
.tri-left {
background-color: green;
margin-left: 2.5em;
clip-path: polygon(100% 100%, 100% 0, 0 50%);
}
.tri-right {
background-color: blue;
clip-path: polygon(0 100%, 0 0, 100% 50%);
}
<div class="sq-tri">
<div class="tri tri-down" data-location="top"></div>
<div class="tri tri-right" data-location="left"></div>
<div class="tri tri-left" data-location="right"></div>
<div class="tri tri-up" data-location="bottom"></div>
</div>

Use an SVG as detailed in this answer
Note: As each of the elements inside the SVG has an ID, you should be able to target them with JS/Jquery.
svg {
display: block;
width: 200px;
height: 200px;
margin: 25px auto;
border: 1px solid grey;
stroke: #006600;
}
#buttons polygon:hover {
fill: orange;
}
#top {
fill: #cc3333;
}
#right {
fill: #663399;
}
#left {
fill: #bada55;
}
<svg viewbox="0 0 100 100">
<g id="buttons">
<polygon id="top" points="0,0 100,0 50,50" />
<polygon id="right" points="100,0 50,50 75,75 100,100" />
<polygon id="bottom" points="0,100 50,50 75,75 100,100" />
<polygon id="left" points="0,0 25,25 50,50 0,100" />
</g>
</svg>

Related

Absolute positioning messed up by CSS rotation

I've made a tiny example below showcasing the behavior I currently get and the behavior I want.
// Rotated div
rotated.style.left = "50px";
rotated.style.top = "100px";
// Original "untouched" div
original.style.left = "50px";
original.style.top = "100px";
// Where the rotated div *should* be
expected.style.left = "-10px";
expected.style.top = "160px";
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
#rotated {
transform: rotateZ(90deg);
background: blue;
}
#original {
background: red;
}
#expected {
transform: rotateZ(90deg);
background: green;
}
<div id="rotated"></div>
<div id="original"></div>
<div id="expected"></div>
The red div is the "original" div that I have not applied any transformations to. The blue div is rotated by 90 degrees. The red and blue div are both shifted by the same values, but clearly their corners don't line up. The green div is the expected (desired) position of the blue div.
As you can see, the left and top is not really working as desired. I understand why it isn't, but I'm looking for some solutions or workarounds. I have searched online and found the transform-origin property but I've got some problems using it. This is because the elements I'm looking to move are created dynamically. They have unknown widths and heights, and on top of that, the widths and heights will change later on!
I know for this static example I can just add transform-origin: 40px 40px; to (which is just the height / 2 twice) div#rotated and it'll work, but in my project that means I'd have to set this property on every element and update it every time I update the element's dimensions.
I just don't think this is that great and I'm looking for one of two possible solutions:
A pure CSS solution that somehow gets the height of the selected element and uses that as the transform-origin (or just any pure CSS solution that works)
Using JavaScript to calculate the corrected position (in this static example, I should be able to get -10, 160 as the position of the element) every time I want to move an element.
--- update ---
This problem is further complicated because if the rotation is 180deg or 270deg then the transform-origin of 40px 40px no longer works. I'd have to compute a new transform-origin every time I want to move an element... This is something I'd really like to avoid...
You could add a translate in your expected transform
.wrapper {
position: relative;
margin: 200px 0 0 200px;
width: 200px;
height: 200px;
border: 1px solid black;
}
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
#rotated {
transform: rotateZ(90deg);
background: blue;
}
#original {
background: red;
}
#expected {
transform: rotateZ(90deg) translateY(-100%);
background: green;
transform-origin: 0 0;
}
<div class="wrapper">
<div id="rotated"></div>
<div id="original"></div>
<div id="expected"></div>
</div>
I put all in a wrapper with a border. Transform origin is from bounding box
second snippet with several transform rotated + translate
if I still didn't understand exactly the exact rotation you need, you can play with parameters translate:
translateY
translateX
translate with 2 values
always check the wrapper around it's the bounding box: 0 0 refers to top left
.wrapper1 {
position: relative;
top: 100px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper2 {
position: relative;
top: 250px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper3 {
position: relative;
top: 400px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
.wrapper4 {
position: relative;
top: 550px;
left: 100px;
width: 200px;
height: 200px;
border: 1px solid black;
}
div {
position: absolute;
height: 80px;
width: 200px;
opacity: 0.5;
mix-blend-mode: overlay;
}
.rotated1 {
transform: rotateZ(90deg);
background: blue;
}
.rotated2 {
transform: rotateZ(90deg) translateY(-100%);
transform-origin: 0 0;
background: blue;
}
.rotated3 {
transform: rotateZ(90deg) translateY(-50%);
transform-origin: 0 0;
background: blue;
}
.rotated4 {
transform: rotateZ(90deg) translate(-50%, -50%);
transform-origin: 0 0;
background: blue;
}
.original {
background: red;
}
<div class="wrapper1">
<div class="rotated1"></div>
<div class="original"></div>
</div>
<div class="wrapper2">
<div class="rotated2"></div>
<div class="original"></div>
</div>
<div class="wrapper3">
<div class="rotated3"></div>
<div class="original"></div>
</div>
<div class="wrapper4">
<div class="rotated4"></div>
<div class="original"></div>
</div>
For now, I've solved this by computing the correct transform-origin from the angle of rotation (which is always 0, 90, 180, or 270). The code is in TypeScript:
export function computeTransformOrigin(element: HTMLElement) {
const { width, height, transform } = getComputedStyle(element);
if (transform && transform !== "none") {
const values = transform.match(/^matrix\((.+)\)$/)?.[1].split(", ");
if (values) {
element.style.translate = "";
const [a, b] = values.map(Number);
const angle = (Math.round(Math.atan2(b, a) * (180 / Math.PI)) + 360) % 360;
if (angle === 0 || angle === 90) return parseFloat(height) / 2 + "px " + parseFloat(height) / 2 + "px";
if (angle === 180) return "center";
element.style.translate = "0 " + (parseFloat(width) - parseFloat(height)) + "px";
return parseFloat(height) / 2 + "px " + parseFloat(height) / 2 + "px";
}
}
return "center";
}
For no rotation or 90 degrees, we can get the transform origin as the height of the element divided by 2 (40px 40px). With 180 degree rotation, we use center, and if it's 270, we have to do some extra magic. The transform origin is the same as 90 degrees but we also have to translate the element down by the width minus the height.
Then when I update the angle for an element, I only need to update the transform origin at the end:
set angle(v: number) {
this.#angle = v % 360;
this.element.style.transform = `rotateZ(${v}deg)`;
if (v === 180) {
this.name.style.transform = `rotateZ(${v}deg)`;
} else {
this.name.style.transform = "";
}
this.element.style.transformOrigin = computeTransformOrigin(this.element);
}

How to make a loading animation that shows up when you click on a button? It should take up the full screen & should be from right to left & disappear

I do have a feeling that this post will get a lot more negative responses as compared to positive ones but it's ok, one response containing the correct answer is worth it!
Ok, this feature is a bit hard to explain in words. I want to add a loading animation whenever a button from the navigation bar is clicked. It should take up 100vh height and 100vw width and should be from right to left and then disappear. (Need help with both CSS and js, maybe HTML too)
I suggest checking out https://www.jacekjeznach.com using a laptop. You can see the really cool loading animation going on when you click on any of the options from the main navigation bar situated on the left side of the website
I know I can't make the exact effect without becoming an expert in web development. I even checked out the GitHub repo of his portfolio but there was no index.html there. A lot of .jsx files (ReactJS) though.
I know the basics of HTML, CSS, JS and never worked with any frameworks (not been more than 2 months since I started learning web dev) but I need help with this project because it is a college assignment.
I chose to make an eLearning website, similar to what this guy teaches(using webflow and few backend tools like MemberStack, Airtable & Zapier): https://www.youtube.com/watch?v=1-_rGcBQLzE&list=PL23ZvcdS3XPINPbP6y06tcLY_rZLi8euf
I am allowed to use any frameworks but I am not allowed to use any website building tools(I can't explain the complex javascript code if I ignore the instructions and use it anyway). Connection to the backend is a plus point but not a requirement.
Currently I am just making the basic homepage of the website and its code is:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<style>
.slide {
width: 97vw;
height: 97vh;
margin: auto;
}
.wrapper {
display: flex;
flex-direction: row;
width: 500vw;
transform: rotate(90deg) translateY(-97vh);
transform-origin: top left;
}
.one {
background: #efdefe;
}
.two {
background: #a3f3d3;
}
.three {
background: rgb(245, 228, 228);
}
.four {
background: #ffddcc;
}
.five {
background: rgb(245, 241, 225);
}
.outer-wrapper {
width: 97vh;
height: 97vw;
margin: auto;
transform: rotate(-90deg) translateX(-97vh);
transform-origin: top left;
overflow-y: scroll;
overflow-x: hidden;
position: absolute;
scrollbar-width: none;
-ms-overflow-style: none;
}
::-webkit-scrollbar {
display: none;
}
.wrap-class {
margin-left: 1vw;
display: flex;
align-items: middle;
justify-content: space-around;
height: 100vh;
width: 10vw;
align-content: space-between;
justify-content: center;
position: fixed;
flex-direction: column;
vertical-align: center;
}
/*Code for the horizontal navbar on left side: */
.navbar {
width: 10vw;
height: auto;
}
.margin1vh {
margin-top: 0.7vh;
margin-bottom: 0.7vh;
}
a:-webkit-any-link {
text-decoration: none;
color: white;
padding: 1vw;
padding-left: 0;
display: block;
/* padding: 3vh 1vw 3vh 1; */
height: 100%;
width: 100%;
}
button {
background-color: black;
display: block;
width: 100%;
box-shadow: inset 2px 2px black, 4px 4px 0 grey;
}
button:hover {
transform: scale(1.1);
}
html {
background-color: black;
/* filter: invert(1); */
scroll-behavior: smooth;
}
/*This code allow us to add linear gradient to a text*/
p,
h1 {
display: block;
margin-left: 31%;
margin-top: 5000px !important;
max-width: 1vw;
background: rgb(2, 0, 36);
background: radial-gradient(circle,
rgba(2, 0, 36, 1) 0%,
rgba(165, 106, 108, 1) 0%,
rgba(175, 99, 99, 1) 0%,
rgba(148, 116, 123, 1) 0%,
rgba(91, 153, 175, 1) 0%,
rgba(62, 172, 200, 1) 0%,
rgba(194, 226, 162, 1) 0%,
rgba(0, 212, 255, 1) 0%,
rgba(18, 255, 21, 1) 14%,
rgba(230, 65, 87, 1) 29%,
rgba(194, 185, 52, 1) 46%,
rgba(43, 83, 210, 1) 64%,
rgba(59, 221, 55, 1) 80%,
rgba(222, 85, 217, 1) 92%);
-webkit-background-clip: text;
background-clip: text;
/*for compatibility with safari browser*/
-webkit-text-fill-color: transparent;
display: inline;
background-size: 300%;
animation: bg-animation 17s infinite;
}
#keyframes bg-animation {
0% {
background-position: left;
}
50% {
background-position: right;
}
100% {
background-position: left;
}
}
/*Now, let's add the animation that happens when a button of fixed position is clicked: */
.animation-on-click {
min-width: 100vw;
min-height: 100vh;
background-color: black;
animation-name: animate;
animation-duration: 2s;
animation-iteration-count: 1;
overflow: visible;
}
#keyframes animate {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(-100%);
}
}
</style>
</head>
<body>
<div class="outer-wrapper">
<div class="wrapper">
<div class="slide one" id="one">
<div>
<br />
<br />
<h1>Welcome to the website</h1>
</div>
</div>
<div class="slide two" id="two">
<div>
<br />
<br />
<h1>Welcome to the eLearning website</h1>
</div>
</div>
<div class="slide three" id="three"></div>
<div class="slide four" id="four"></div>
<div class="slide five" id="five"></div>
</div>
</div>
<div class="wrap-class">
<div class="navbar">
<button>
Home
</button>
<div class="margin1vh"></div>
<button>
About
</button>
<div class="margin1vh"></div>
<button>
Website
</button>
<div class="margin1vh"></div>
<button>
Support
</button>
<div class="margin1vh"></div>
<button>
Contact
</button>
</div>
</div>
<script>
function clicked() {
var element = document.getElementById("one");
element.classList.add("animation-on-click");
setTimeout(function () {
element.classList.remove("animation-on-click");
}, 2000);
}
</script>
</body>
</html>
I just need a loading animation (without a loading bar should also work) but it should be done on the whole screen. I think I might change the background color of all the elements in CSS horizontal flexbox to black also because black is the best background and it will allow me to change width ad height attributes of the .slide class and translateX and translateY functions from 97vw, 97vh to 100vw, 100vh (as they were in the original code)
Btw, I have combined the codes of CSS and JS files in the HTML file here to be able to share the code here on StackOverflow.
You can visit https://github.com/shubham-garg1/web-project to check the GitHub code. I have also published the work done till now online so you can go to http://www.elearningweb.tk and check the sources files.
Any help is appreciated, thanks.
They're actually pretty easy to do if you just want a spinner, but a "loading" animation should be tied to a "loading" process. Without making any assumptions, lets consider some native Javascript Ajax call...
var xmlhttp = new XMLHttpRequest();
document.getElementById("myLoader").style.display = "block";
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
document.getElementById("myLoader").style.display = "none";
}
}
};
xmlhttp.open("GET", "https://www.httpbin.org/get", true);
xmlhttp.send();
You can just throw an animation inside your myLoader div... If you want to get a little fancy, you can use Javascript to animate it after you load it.
Let's grab some code: I'll grab it from https://www.w3schools.com/howto/howto_css_flip_card.asp.
It's just a card flip on hover, but I've removed the hover part.
The important part here is in the flip-card-inner portion of the CSS. It's going to dictate to Javascript how long the animation process should take. If you want a longer one, just adjust the transition value.
JsFiddle: https://jsfiddle.net/L1fk0nhm/
<style>
.flip-card {
background-color: transparent;
width: 300px;
height: 200px;
border: 1px solid #f1f1f1;
perspective: 1000px;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
text-align: center;
transition: transform 0.8s;
transform-style: preserve-3d;
}
.flip-card-front, .flip-card-back {
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden; /* Safari */
backface-visibility: hidden;
}
.flip-card-front {
background-color: #bbb;
color: black;
}
.flip-card-back {
background-color: dodgerblue;
color: white;
transform: rotateY(180deg);
}
</style>
<div class="flip-card">
<div id="myLoader" class="flip-card-inner">
<div class="flip-card-front">
I'm hiding stuff that's loading!
</div>
<div id="loadedStuff" class="flip-card-back">
</div>
</div>
</div>
We can actually have it flip on load, using Javascript after the inside of the element has loaded.
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
document.getElementById("loadedStuff").innerHTML = xmlhttp.responseText;
animatedCardRotation(document.getElementById("myLoader"), 0, 180);
}
}
};
xmlhttp.open("GET", "https://www.httpbin.org/get", true);
xmlhttp.send();
function animatedCardRotation (Element, startDegree, endDegree) {
if(startDegree < endDegree ) {
startDegree += 10;
Element.style.transform = `rotateY(${startDegree}deg)`;
requestAnimationFrame( () => this.animatedCardRotation(Element, startDegree, endDegree) );
}
}
Combining requestAnimationFrame here really adds a lot to your CSS animations. You can virtually do any kind of CSS transformation here and if you have something that loads in chunks, it can help you build a very accurate "loading bar".

How to create curve lines css div? [duplicate]

This question already has an answer here:
How to create a curved line with gradient?
(1 answer)
Closed 3 years ago.
I am using css, div only. I am trying to draw these type of lines :
.line {
width: 1px;
height: 100px;
background-color: black;
position: absolute;
border-radius: 50%/100px 100px 0 0;
}
#line1 {
top: 100px;
left: 50px;
}
#line2 {
top: 220px;
left: 150px;
height: 115px;
transform: rotate(120deg);
-webkit-transform: rotate(120deg);
-ms-transform: rotate(120deg);
}
<div class="line" id="line1"></div>
<div class="line" id="line2"></div>
I am trying to use border-radius: 50%/100px 100px 0 0; but no idea what is going wrong as nothing happens. Sorry for bad English,this is what i am trying to do. Please help.
You could use SVGs to achieve what you want.
See code below
Read more here -> SVG
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 5 50 C 40 10, 65 10, 75 40 S 100 100, 180 20" stroke="black" fill="transparent"/>
</svg>
EDIT: if you can't use SVG or other solutions besides div elements. and css you could use this.
If you can use ONLY 1 DIV element. Then use the below css on pseudo-elements before and after instead of line1 and line2
.line1 {
border-radius:100px 0 0 0 ;
border-width: 2px 0 0 2px;
margin-left:100px;
}
.line2 {
border-radius:0 0 100px 0 ;
border-width: 0 2px 2px 0 ;
}
.line {
border-color:red;
border-style: solid;
height:100px;
width: 100px;
}
<div class="line line1"></div>
<div class="line line2"></div>

css triangle based on page height

Currently I have the situation as shown below in the snippet.
But now I want a triangle that is the same on every page. No matter how long the page is. So for example if the page is really long, then the triangle will at one point go out of the screen and there will be no more green background. (as shown here)
But the most important thing is that on every page the triangle/angle will be the same. How to do this?
$(document).ready(function() {
function waitForElement(elementPath, callBack) {
window.setTimeout(function() {
if ($(elementPath).length) {
callBack(elementPath, $(elementPath));
} else {
waitForElement(elementPath, callBack);
}
}, 300)
}
waitForElement("#leftdiv", function() {
// Initial background height set to be equal to leftdiv
$('#rightdiv').height($('#leftdiv').height());
// Initial triangle height set to be equal to leftdiv
$('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
});
// When window resizes
$(window).resize(function() {
// Change height of background
$('#rightdiv').height($('#leftdiv').height());
// Change height of white triangle
$('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
});
});
.container-general {
float: left;
position: relative;
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
float: right;
position: relative;
/*height is set in javascript*/
width: 30%;
background-color: green;
}
#triangle {
position: absolute;
height: 0;
width: 0;
bottom: 0;
left: -1px;
border-left: 10vw solid white;
border-right: 0px solid transparent;
/*border-top is set in javascript*/
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div id="triangle"></div>
</div>
You don't need JavaScript and jQuery at all for this, as long as you are willing to make minor changes to your markup:
Step 1: Update your markup
Wrap both your .container-general and .background-general with a common parent element
Use display: flex; overflow: hidden; on the parent. This has the effect of stretching the shorter background element to full height of .container-general
Step 2: Determine the fixed angle you want and set aspect ratio
Important note: If you want to keep the angle constant, you will need to know what angle you want. That will require one important trick: you want to keep .background-general the same aspect ratio in all cases, so the angle stays constant. Let's say you want it to be 60° (i.e. Math.Pi / 3): with some math, that means that the height of the .background-general should be this ratio relative to the width:
containerHeightRatioToWidth = Math.tan(Math.PI / 3) = 1.732052602783882...
There is a trick to preserve the aspect ratio: you simply set the padding-bottom of the background element. In this case, you want it to be padding-bottom: 173%); (we don't need absolute precision so we can drop the decimal points).
Here's a handy table on the height (in CSS percentages) you can use:
30deg: padding-bottom: 57%:
45deg: padding-bottom: 100%:
60deg: padding-bottom: 173%:
You can also precalculate the percentage in your browser console by pasting this:
var desiredAngleInDegrees = 60;
Math.tan(Math.PI * desiredAngleInDegrees / 180) * 100
The markup is structured as follows:
└─┬.wrapper
├──.container-general
└─┬.background-general
└─┬.background-general__background
├─::before (triangle)
└─::after (remaining fill)
To achieve the triangle effect, you have two approaches:
Step 3A: Use clip-path to trim the background element to look like a triangle
clip-path is very widely supported by modern browsers, with a notable exception for IE11 and Edge :/ This should do the trick: clip-path: polygon(100% 0, 0 0, 100% 100%);
.wrapper {
display: flex;
overflow: hidden;
}
.container-general {
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
position: relative;
width: 30%;
background-color: green;
overflow: hidden;
}
.background-general__background {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/* Triangle */
.background-general__background::before {
flex-grow: 0;
content: '';
display: block;
width: 100%;
padding-bottom: 173%;
background-color: white;
clip-path: polygon(0 100%, 0 0, 100% 100%);
}
/* Extra fill */
.background-general__background::after {
flex-grow: 1;
content: '';
display: block;
background-color: white;
/* Needed to fix subpixel rendering */
margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div class="background-general__background"></div>
</div>
</div>
Step 3B: Use an inline SVG as background image
For the greater browser compatibility, use an inline encoded SVG and stretch it to 100% width and 100% height of the parent.
We can create a simple 10×10px SVG of the following markup:
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10">
<path fill="green" d="M0,0 L10,0 L10,10 z"></path>
</svg>
Note: The preserveAspectRatio="none" is required so that we can freely stretch the SVG beyond its usual aspect ratio. For more information of how the <path>'s d attribute works, see this article: The SVG path Syntax: An Illustrated Guide
Then, all you need is to stuff this short SVG markup as data:image/svg+xml for the background image of the background container, i.e.:
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="green" d="M0,0 L10,0 L10,10 z"></path></svg>');
See example below:
.wrapper {
display: flex;
overflow: hidden;
}
.container-general {
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
position: relative;
width: 30%;
background-color: green;
overflow: hidden;
}
.background-general__background {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/* Triangle */
.background-general__background::before {
content: '';
display: block;
flex-grow: 0;
width: 100%;
padding-bottom: 173%;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="white" d="M0,0 L0,10 L10,10 z"></path></svg>');
background-size: 100% 100%;
}
/* Extra fill */
.background-general__background::after {
flex-grow: 1;
content: '';
display: block;
background-color: white;
/* Needed to fix subpixel rendering */
margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div class="background-general__background"></div>
</div>
</div>
A simple "border triangle" bind to vw units might do:
body {
min-height: 2000px;
}
#triangle {
position: absolute;
top: 0px;
right: 0px;
border-top: 100vw solid #ff0000; /* The height of the triangle */
border-left: 30vw solid transparent; /* The width of the triangle */
}
<div id="triangle"></div>
A fiddle to play with.

max scale in middle instead of end

In the following snippet, I am trying to achieve an effect where the div which appears in the middle of the visible scroll section is at full scale scale(1); and the other div's scale falloff towards scale(0); as they approach the edges.
I have drawn a debug box in the middle where the full scale div should appear.
var viewport = {
x: $("#scroll").scrollLeft(),
width: $("#scroll").width(),
}
$("#scroll").scroll(function() {
viewport.x = $("#scroll").scrollLeft();
recalculateScale();
});
recalculateScale();
function recalculateScale() {
$("#example > div").each(function() {
let middleOfThis = ($(this).position().left + ($(this).width() * 0.5)); // calculate from the middle of each div
let scale = Math.sin(middleOfThis / $("content").width());
$(this).css('transform', 'scale(' + scale + ')');
});
}
content {
position: relative;
display: block;
width: 500px;
height: 100px;
}
content::after {
content: '';
position: absolute;
display: block;
width: 20%;
height: 100%;
left: 50%;
top: 0;
transform: translateX(-50%);
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
#scroll {
display: block;
width: 100%;
height: 100%;
overflow-x: scroll;
border: 1px solid #000;
}
#example {
display: block;
width: 200%;
height: 100%;
font-size: 0;
overflow: none;
}
#example>div {
display: inline-block;
width: 10%;
height: 100%;
background-color: #f00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<content>
<div id="scroll">
<section id="example">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</section>
</div>
</content>
Currently the scale is spanning from far left to right of #example. I know I need to factor the viewport dimensions into the equation before Math.sin is evaluated, I just can't get it quite right.
Note: no arrow functions because I have to target IE11.
Two issues:
While .position().left returns the rendered position of the element after scaling, .width() returns the element's width without taking the scaling into account. Obviously such different way of measurement will lead to a wrong calculation of the middle point. Use .getBoundingClientRect().width instead: that will take the current scaling into account
When using trigonometric functions, you need to make sure the argument represents an angle expressed in radians. In your code, the value ranges from 0 to 1, while the sine takes its maximum value not at 0.5, but at π/2. So you should perform a multiplication with π to get the desired result.
Here is the adapted code:
var viewport = {
x: $("#scroll").scrollLeft(),
width: $("#scroll").width(),
}
$("#scroll").scroll(function() {
viewport.x = $("#scroll").scrollLeft();
recalculateScale();
});
recalculateScale();
function recalculateScale() {
$("#example > div").each(function() {
// 1. Use different way to read the width: this will give the rendered width
// after scaling, just like the left position will be the actually rendered
// position after scaling:
let middleOfThis = $(this).position().left
+ this.getBoundingClientRect().width * 0.5;
// 2. Convert fraction to a number of radians:
let scale = Math.sin(middleOfThis / $("content").width() * Math.PI);
$(this).css('transform', 'scale(' + scale + ')');
});
}
content {
position: relative;
display: block;
width: 500px;
height: 100px;
}
content::after {
content: '';
position: absolute;
display: block;
width: 20%;
height: 100%;
left: 50%;
top: 0;
transform: translateX(-50%);
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.5);
}
#scroll {
display: block;
width: 100%;
height: 100%;
overflow-x: scroll;
border: 1px solid #000;
}
#example {
display: block;
width: 200%;
height: 100%;
font-size: 0;
overflow: none;
}
#example>div {
display: inline-block;
width: 10%;
height: 100%;
background-color: #f00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<content>
<div id="scroll">
<section id="example">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</section>
</div>
</content>
NB: Because of floating point precision limitations, the calculation of the mid points could slide away with little fractions. This will be so tiny, that it should not make a difference in actual pixel distance, but it would not hurt to pre-calculate the centres of the elements, so that you always use the same value.

Categories

Resources