admin管理员组文章数量:1291007
I'm trying to make a chat app and at the press of a button you can see timestamps of messages slide in.
My timestamp always takes 200px in size and the other takes the rest.
Let's call div1 for the timestamp (200px) and div2 for the messages (takes up the rest of the space).
I tried using the translate on the first div1 and to move and quickly realised that the div2 won't resize automatically:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*{
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.chat-container {
display: flex;
height: 50vh;
width: 50vw;
background-color: #f0f0f0;
}
#div1 {
background-color: #0004ff;
height: 100%;
width: 200px;
text-align: center;
position: relative;
left:-200px;
transition: left 1s;
}
#div2 {
background-color: #ff0000;
height: 100%;
text-align: center;
width: calc(100% - 200px); /* 100% - 200px or auto would work*/
}
</style>
</head>
<body>
<div class="chat-container">
<div id="div1">
<p>div1</p>
</div>
<div id="div2">
<p>div2</p>
</div>
</div>
<button id="button">animation</button>
<script>
let actived = false;
document.getElementById("button").addEventListener("click", function() {
const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");
div1.style.left = actived ? "-200px" : "0px";
actived = !actived;
});
</script>
</body>
</html>
So I tried resizing the size of the div2 but when then I did calc(100% - 200px)
it wasn't the same size!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*{
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.chat-container {
display: flex;
height: 50vh;
width: 50vw;
background-color: #f0f0f0;
overflow: hidden;
}
#div1 {
background-color: #0004ff;
height: 100%;
width: 200px;
text-align: center;
position: relative;
left:-200px;
transition: left 1s;
}
#div2 {
background-color: #ff0000;
height: 100%;
text-align: center;
width: calc(100% - 200px); /* 100% - 200px or auto would work*/
position: relative;
transition: width 1s;
}
</style>
</head>
<body>
<div class="chat-container">
<div id="div1">
<p>div1</p>
</div>
<div id="div2">
<p>div2</p>
</div>
</div>
<button id="button">animation</button>
<script>
let actived = false;
document.getElementById("button").addEventListener("click", function() {
const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");
div1.style.left = actived ? "-200px" : "0px";
div2.style.width = actived ? "100%" : "calc(100% - 200px)";
actived = !actived;
});
</script>
</body>
</html>
How can 100%
of the parent minus the div1's size isn't the rest? I'm so confused.
Please tell me what method I should use and why 100% - 200px
doesn't line up.
I'm trying to make a chat app and at the press of a button you can see timestamps of messages slide in.
My timestamp always takes 200px in size and the other takes the rest.
Let's call div1 for the timestamp (200px) and div2 for the messages (takes up the rest of the space).
I tried using the translate on the first div1 and to move and quickly realised that the div2 won't resize automatically:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*{
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.chat-container {
display: flex;
height: 50vh;
width: 50vw;
background-color: #f0f0f0;
}
#div1 {
background-color: #0004ff;
height: 100%;
width: 200px;
text-align: center;
position: relative;
left:-200px;
transition: left 1s;
}
#div2 {
background-color: #ff0000;
height: 100%;
text-align: center;
width: calc(100% - 200px); /* 100% - 200px or auto would work*/
}
</style>
</head>
<body>
<div class="chat-container">
<div id="div1">
<p>div1</p>
</div>
<div id="div2">
<p>div2</p>
</div>
</div>
<button id="button">animation</button>
<script>
let actived = false;
document.getElementById("button").addEventListener("click", function() {
const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");
div1.style.left = actived ? "-200px" : "0px";
actived = !actived;
});
</script>
</body>
</html>
So I tried resizing the size of the div2 but when then I did calc(100% - 200px)
it wasn't the same size!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
*{
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.chat-container {
display: flex;
height: 50vh;
width: 50vw;
background-color: #f0f0f0;
overflow: hidden;
}
#div1 {
background-color: #0004ff;
height: 100%;
width: 200px;
text-align: center;
position: relative;
left:-200px;
transition: left 1s;
}
#div2 {
background-color: #ff0000;
height: 100%;
text-align: center;
width: calc(100% - 200px); /* 100% - 200px or auto would work*/
position: relative;
transition: width 1s;
}
</style>
</head>
<body>
<div class="chat-container">
<div id="div1">
<p>div1</p>
</div>
<div id="div2">
<p>div2</p>
</div>
</div>
<button id="button">animation</button>
<script>
let actived = false;
document.getElementById("button").addEventListener("click", function() {
const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");
div1.style.left = actived ? "-200px" : "0px";
div2.style.width = actived ? "100%" : "calc(100% - 200px)";
actived = !actived;
});
</script>
</body>
</html>
How can 100%
of the parent minus the div1's size isn't the rest? I'm so confused.
Please tell me what method I should use and why 100% - 200px
doesn't line up.
2 Answers
Reset to default 2Explanation
The difference when toggling is due to implicit flex-shrink
behavior (since elements have a default of flex-shrink: 1
in flex layouts:
The
flex-shrink
property manages removing negative free space to make boxes fit into their container without overflowing. Removing space is a bit more complicated than adding space. The flex shrink factor is multiplied by the flex base size; this distributes negative space in proportion to how much the item can shrink. This prevents smaller items from shrinking to0px
before a larger item is noticeably reduced.
This may be better demonstrated through example.
Say chat-container
is 500px
wide.
The intrinsic widths of the elements before any flex shrinking has taken place is:
div1 | div2 |
---|---|
200px |
100% - 200px = 500px - 200px = 300px |
No shrinking happens, everything fits exactly.
Click the animation button twice and then div2's width
is now 100%
, thus:
div1 | div2 |
---|---|
200px |
100% = 500px |
Now, div1 and div2 are now wider than the container, since changing left
on div1 does not remove the space it takes up. Thus, flex shrinking calculations happen as follows:
Calculate the surplus widths of our child elements:
200px + 500px - 500px = 200px;
↑ ↑ ↑
div1 div2 container
The ratio of div1:div2 is 2:5
, thus we allocate this 200px
surplus between them as follows:
div1 | div2 |
---|---|
200px ÷ 7 × 2 ≈ 57px |
200px ÷ 7 × 5 ≈ 143px |
Thus the final widths are as follows:
div1 | div2 |
---|---|
143px |
357px |
Solution
If you want to actually slide the div1 in and out, you can use margin
, and let div2 fill up the space naturally with flex-grow: 1
:
let actived = false;
document.getElementById("button").addEventListener("click", function() {
const div1 = document.getElementById("div1");
div1.style.marginLeft = actived ? "-200px" : "0px";
actived = !actived;
});
* {
padding: 0px;
margin: 0px;
box-sizing: border-box;
}
.chat-container {
display: flex;
height: 50vh;
width: 50vw;
background-color: #f0f0f0;
overflow: hidden;
}
#div1 {
background-color: #0004ff;
height: 100%;
width: 200px;
text-align: center;
position: relative;
margin-left: -200px;
transition: margin-left 1s;
}
#div2 {
background-color: #ff0000;
height: 100%;
text-align: center;
flex-grow: 1;
}
<div class="chat-container">
<div id="div1">
<p>div1</p>
</div>
<div id="div2">
<p>div2</p>
</div>
</div>
<button id="button">animation</button>
There are a lot of parts that go into a chat app so I made a simple one and then animated the rows as they are made. A simple animation can break easily if the layout is complex, elements are added dynamically, or if there's scrolling involved etc. Also, the first column is 40% width instead of 200px because absolute widths are not responsive. Details are commented in example. View in Full page mode.
BTW, mathematically calc(100% - 200px)
should work but if the parent has left and/or right padding or if there's a gap
property or if there's a scrollbar etc. you need to include that in the formula as well.
// Reference <form>
const chat = document.forms.chat;
/*
Reference all:
- <fieldset>
- <textarea>
- <button>
*/
const io = chat.elements;
// Reference fieldset#scroller
const scr = io.scroller;
// Reference fieldset#box
const box = io.box;
// Reference <textarea>
const msg = io.message;
// Register <form> to listen for the "submit" event.
chat.addEventListener("submit", send);
/**
* Creates a current timestamp in the following format:
* YY-MM-DD HH:MM:SS
* @return {string} - YY-MM-DD HH:MM:SS
*/
function tStamp() {
const now = new Date()
let dT = Date.parse(now.toISOString().slice(0, -5));
return new Date(dT - (now.getTimezoneOffset() * 120000)).toISOString().slice(2, -5).split("T").join(" ");
}
/**
* Adds a given string (@param br) at the end of every
* given number of characters (@param max) in a given
* string (@param string).
* @param {string} string - String to add line breaks to
* @param {number} max - Number of characters per line.
* @param {string} br - String to add to the end of
* @default "\n" each line.
* @return {string} - String with line breaks
* https://www.30secondsofcode./js/s/word-wrap/
*/
function lBreak(string, max, br = '\n') {
const rgx = new RegExp(`(?![^\\n]{1,${max}}$)([^\\n]{1,${max}})\\s`, 'g');
return string.replace(rgx, `$1${br}`);
}
/**
* "submit" event handler creates a two column row consisting of
* a timestamp and the text message the user submitted.
* The .expand class is added to each column to animate them.
* Scrolling is kept at the bottom of fieldset#scroller.
* textarea#message is cleared.
* @param {object} e - Event object
*/
function send(e) {
e.preventDefault();
const time = document.createElement("fieldset");
time.className = "time";
time.innerHTML = `
<time>${tStamp()}</time>`;
const text = document.createElement("fieldset");
text.className = "text";
text.innerHTML = `
<p><b>User </b>${lBreak(msg.value, 32)}</p>`;
box.append(time, text);
setTimeout(() => {
time.classList.add("expand");
}, 500);
setTimeout(() => {
text.classList.add("expand");
}, 750);
setTimeout(() => {
box.lastElementChild.scrollIntoView({
behavior: "smooth"
});
}, 1000);
msg.value = "";
}
:root {
font: 2ch/1.3 "Segoe UI";
overflow-x: hidden;
}
form {
width: 50vw;
margin: 0 auto;
padding: 8px;
border-radius: 8px;
background: #677680;
}
fieldset {
padding: 0;
border: 0;
}
p {
margin: 0;
padding: 0 5px;
}
.frame {
padding: 5px 5px 0;
border-radius: 8px;
background: #aec7d6;
overflow: hidden;
}
#scroller {
display: flex;
flex-direction: column;
height: 50vh;
overflow-y: scroll;
overflow-x: hidden;
}
#box {
flex: 1;
display: flex;
flex-wrap: wrap;
align-items: center;
align-content: flex-start;
gap: 3px;
height: min-content;
background: #677680;
}
/*
width, max-width, font-size, and opacity are animated for
all fieldset.time and fieldset.text when the .expand class
is assigned to them. A ✱ denotes the relevant rulesets.
*/
.time {
width: 0; /* ✱ */
max-width: 0; /* ✱ */
margin-left: 3px;
font-size: 0; /* ✱ */
font-family: Consolas;
color: #fff;
background: transparent;
transition: all 1.4s; /* ✱ */
opacity: 0; /* ✱ */
overflow: hidden; /* ✱ */
}
.text {
width: 0; /* ✱ */
height: auto;
max-width: 0; /* ✱ */
border-radius: 4px;
background: #dae9f2;
transition: max-width 1.2s; /* ✱ */
overflow-y: auto;
}
.expand {
font-size: 1rem; /* ✱ */
opacity: 1; /* ✱ */
}
.expand.time {
width: 40%; /* ✱ */
max-width: 40%; /* ✱ */
height: 1.3rem;
}
.expand.text {
width: 55%; /* ✱ */
max-width: 55%; /* ✱ */
}
.control {
display: grid;
grid-template-columns: 2fr 1fr;
margin-top: 5px;
}
#message {
border: 0;
border-radius: 0 0 8px 8px;
background: transparent;
resize: none;
}
button {
position: relative;
z-index: 1;
margin-right: -8px;
border: 0;
border-radius: 0 0 8px 8px;
background: #37b35a;
cursor: pointer;
}
<form id="chat">
<fieldset class="frame">
<fieldset id="scroller">
<fieldset id="box"></fieldset>
</fieldset>
<fieldset class="control">
<textarea id="message" placeholder="Type your message here..."></textarea>
<button>Send</button>
</fieldset>
</fieldset>
</form>
本文标签: javascriptHow do I resize two flexboxes in an animationStack Overflow
版权声明:本文标题:javascript - How do I resize two flexboxes in an animation? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741516225a2382905.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论