英文:
css transform:scale property not working on modal image when changing display property
问题
我创建了一个类似于这个的模态图像项目。
当我点击图像时,我希望显示并放大模态图像,当我点击关闭按钮时,它应该缩小并消失。但是,当我使用display
属性来隐藏和显示模态图像时,transform: scale
属性不起作用,模态图像会突然显示和隐藏。
我已经注释掉了document.getElementById("modalimg").style.display = "none";
,这确实改善了情况,但还不够。
我还尝试使用visibility
属性而不是display
属性运行它,它效果很好 - 但我想知道问题是什么。
注意:代码片段需要在全屏模式下运行才能正确显示。
英文:
I created a modal image project similar to this.
When I click on the image I want to display and scale up the modal image, and it should scale down and disappear when I click the close button. However, when I use the display property for hiding and displaying the modal image, the transform: scale
property doesn't work, and the modal image will be displayed and hidden suddenly.
I commented out document.getElementById("modalimg").style.display = "none";
which did improve things, but not enough.
I also tried to run this with the visibility property instead of the display property and it worked well - but I want to know what the problem is.
I also searched for similar questions, and found this and this, but they didn't help.
Note: the code snippet will need to be run in fullscreen to display properly.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
document.getElementById("container").onclick = modal;
function modal(buttFlag) {
document.getElementById("modalimg").style.display = "inline";
document.getElementById("modalbackground").style.display = "block";
document.getElementById("butt").style.display = "block";
document.getElementById("modalimg").style.transition = "all 1s";
document.getElementById("modalimg").style.transform = "scale(7,4)";
if (buttFlag == true) {
document.getElementById("modalimg").style.transform = "scale(1,1)";
document.getElementById("modalimg").style.display = "none";//I tried comment this line and it worked better but not good enough.
document.getElementById("butt").style.display = "none";
document.getElementById("modalbackground").style.display = "none";
buttFlag = false;
}
}
<!-- language: lang-css -->
#container{
width: 20%; height: 50%;
margin: 70%; margin-top: 12%;
position: absolute;
}
#container img{
width: 100%; height: 100%;
object-fit: cover;
}
#modalbackground{
position: absolute;
width: 100%;height: 100%;
background-color: black;
opacity: 0.7;
display: none;
z-index: +1;
}
#modalimg{
width: 100px; height: 100px;
position: absolute;
top:45%; left: 45%;
z-index: +1;
display: none;
}
button{
font-size: 2em;
background-color: transparent;
border: none;
position: absolute;
left: 85%; top: 5%;
color: white;
z-index: +1;
display: none;
}
<!-- language: lang-html -->
<div id="modalbackground"></div>
<div id="container">
<img src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
</div>
<button onclick="modal(true)" id="butt">&#9746;</button>
<img id="modalimg" src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
<!-- end snippet -->
答案1
得分: 1
以下是您要翻译的部分:
问题原因
当元素具有 display: none
时,它不仅仅是在视图中隐藏,浏览器在 DOM 中根本不包括它。此外,当 JavaScript 应用内联样式更改时,浏览器不会逐个渲染它们,而是等待一小段时间,然后一次性渲染它们。
因此,当您有一个具有 display: none
的元素时,如果执行以下操作:
element.style.display = 'block';
element.style.transform = 'scale(2)';
浏览器不会在第一行停下来绘制元素,然后转到下一行并应用 scale(2)
的内联样式。它会在内部表示中准备所有内容,并在决定有时间时将整个元素放入其中。因此,您不会得到一个从 scale(1)
开始并动画到 scale(2)
的元素,该元素已经以 scale(2)
渲染在页面上。
解决问题
要解决这个问题,您必须应用 display: block
,然后等待 一小段时间,直到我们确信浏览器已经绘制了元素。一旦我们确定元素已经绘制到屏幕上(在 scale(1)
处),然后应用样式 transform: scale(2)
。这将进行动画处理。
有内置的方法可以确切知道要等待多长时间,但一个简单的经验法则是记住,60Hz 屏幕大约每 16ms 刷新一次,所以如果等待 20ms,应该没问题。在 JavaScript 中等待指定长度的时间的标准方法是使用 setTimeout。我们先前的代码将如下所示:
element.style.display = 'block';
setTimeout( function() {
element.style.transform = 'scale(2)';
}, 20 );
反向操作
这对于动画模型的出现非常有效,但关闭模态时怎么办?
嗯,原理相同,但是相反。这次您必须等待的时间长度是动画运行的时间,以便在要进行动画时不会出现 display: none
。如果我们的 transform
过渡持续 1 秒,那么上面的代码在使其消失时将如下所示:
element.style.transform = 'scale(2)';
setTimeout( function() {
element.style.display = 'block'; // 将等待 1000ms 运行
}, 1000 );
应用到您的代码
将此应用到您的代码将如下所示:(请注意;在调用模态时存在一些错误,已经修复,但与一般思路无关)。
<!-- 开始代码片段:js 隐藏:false 控制台:true Babel:false -->
<!-- 语言:lang-js -->
document.getElementById("container").onclick = modal;
function modal(buttFlag) {
const modalimg = document.getElementById("modalimg");
const modalbackground = document.getElementById("modalbackground");
const butt = document.getElementById("butt");
if ( buttFlag !== true ) { // 不是关闭按钮
modalbackground.style.display = "block";
modalimg.style.display = "block";
butt.style.display = "block";
setTimeout( function() {
modalimg.style.transform = "scale(7,4)";
}, 20 );
}
if ( buttFlag === true ) { // 是关闭按钮
modalimg.style.transform = "scale(1)";
setTimeout( function() {
modalimg.style.display = "none";
modalbackground.style.display = "none";
butt.style.display = "none";
}, 1000 );
}
}
<!-- 语言:lang-css -->
#container{
width: 20%; height: 50%;
margin: 70%; margin-top: 12%;
position: absolute;
}
#container img{
width: 100%; height: 100%;
object-fit: cover;
position: relative;
z-index: 5;
}
#modalbackground{
position: absolute;
width: 100%;height: 100%;
background-color: black;
opacity: 0.7;
display: none;
z-index: 10;
}
#modalimg{
width: 100px; height: 100px;
position: absolute;
top:45%; left: 45%;
z-index: 11;
display: none;
transform: scale(1);
transition: transform 1s;
}
button{
font-size: 2em;
background-color: transparent;
border: none;
position: absolute;
left: 85%; top: 5%;
color: white;
z-index: 12;
display: none;
}
<!-- 语言:lang-html -->
<div id="modalbackground"></div>
<div id="container">
<img src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
</div>
<button onclick="modal(true)" id="butt">☒</button>
<img id="modalimg" src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
<!-- 结束代码片段 -->
英文:
Why the issue happens
When an element has display: none
, it's not just hidden from view, the browser doesn't include it at all in the DOM. Furthermore, when inline styling changes are applied by JavaScript, the browser doesn't render them one at a time, but waits a short while and then renders them all at once.
So when you have a element with display: none
, if you do
element.style.display = 'block';
element.style.transform = 'scale(2);
the browser won't stop at the first line to draw the element, and then go to the next line and apply the inline style of scale(2)
. It will prepare everything there in its internal representation, and when it decides that it's got time it will dump the entire element in. Therefore, you don't get an element that starts at scale(1)
and gets animated to scale(2)
, the element is drawn on the page already rendered at scale(2)
.
Solving the issue
To get around this, you have to apply display: block
, and then wait a little while until we're sure the browser has drawn the element. Once we're sure the element has been drawn to the screen (at scale(1)
) we then apply the style transform: scale(2)
. This will get animated.
There are built in ways to know exactly how long to wait, but an easy rule of thumb is to remember that 60Hz screens refresh roughly once every 16ms, so if you wait 20ms you should be good. The standard way to wait a specified length of time in JavaScript is with setTimeout. Our previous code would look like:
element.style.display = 'block';
setTimeout( function() {
element.style.transform = 'scale(2)';
}, 20 );
Reversing it
This is all well and good for animating the appearing of the model, but what about when you close the modal?
Well it's just the same, but in reverse. This time the length of time you have to wait is how long the animation runs, so that you don't get display: none
running when you want the animation to be happening. If our transform
transition was to last for 1 second, our code from above would look like the following when making it disappear:
element.style.transform = 'scale(2)';
setTimeout( function() {
element.style.display = 'block'; // will wait 1000ms to run
}, 1000 );
Applying to your code
Applying this to your code would look like the following: (note; there were a couple of bugs when calling the modal that have been patched up, but they aren't relevant to the general idea).
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
document.getElementById("container").onclick = modal;
function modal(buttFlag) {
const modalimg = document.getElementById("modalimg");
const modalbackground = document.getElementById("modalbackground");
const butt = document.getElementById("butt");
if ( buttFlag !== true ) { // not the close button
modalbackground.style.display = "block";
modalimg.style.display = "block";
butt.style.display = "block";
setTimeout( function() {
modalimg.style.transform = "scale(7,4)";
}, 20 );
}
if ( buttFlag === true ) { // is te close button
modalimg.style.transform = "scale(1)";
setTimeout( function() {
modalimg.style.display = "none";
modalbackground.style.display = "none";
butt.style.display = "none";
}, 1000 );
}
}
<!-- language: lang-css -->
#container{
width: 20%; height: 50%;
margin: 70%; margin-top: 12%;
position: absolute;
}
#container img{
width: 100%; height: 100%;
object-fit: cover;
position: relative;
z-index: 5;
}
#modalbackground{
position: absolute;
width: 100%;height: 100%;
background-color: black;
opacity: 0.7;
display: none;
z-index: 10;
}
#modalimg{
width: 100px; height: 100px;
position: absolute;
top:45%; left: 45%;
z-index: 11;
display: none;
transform: scale(1);
transition: transform 1s;
}
button{
font-size: 2em;
background-color: transparent;
border: none;
position: absolute;
left: 85%; top: 5%;
color: white;
z-index: 12;
display: none;
}
<!-- language: lang-html -->
<div id="modalbackground"></div>
<div id="container">
<img src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
</div>
<button onclick="modal(true)" id="butt">&#9746;</button>
<img id="modalimg" src="https://st4.depositphotos.com/11639344/22517/i/600/depositphotos_225179058-stock-photo-asian-rainforest-jungle-august.jpg" alt="jungle">
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论