ข้ามการเปลี่ยนแปลง CSS ชั่วคราว

สมมติว่าฉันมีสไตล์แบบนี้ โดยมีการเปลี่ยนแปลง:

#theElement {
    position: relative;
    left: 200px;
    transition: left 1s;
}

และรหัสบางส่วน:

var element = document.getElementById("theElement");

function animateX(px) {
    element.style.left = px + "px";
}

ดังนั้น เพียงแค่มีฟังก์ชัน animateX ซึ่งเพียงบอกว่ามันทำอะไร เคลื่อนไหวคุณสมบัติทางซ้ายของ theElement แล้วตอนนี้จะเป็นอย่างไรถ้าฉัน ด้วย ต้องการมีฟังก์ชันที่ตั้งค่าคุณสมบัติทางซ้ายทันที โดยไม่มีการเปลี่ยนแปลง : :

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    element.style.transition = "left 1s";
}

เหตุใดจึงใช้งานไม่ได้และฉันจะแก้ไขได้อย่างไร


person Community    schedule 11.01.2016    source แหล่งที่มา


คำตอบ (4)


เพื่อให้ทำงานได้ คุณต้องล้างการเปลี่ยนแปลง CSS โดยการอ่านการเปลี่ยนแปลงใน JS คำตอบสำหรับคำถามนี้อธิบายวิธีการทำงาน:

ฉันสามารถใช้จาวาสคริปต์ได้ไหม เพื่อบังคับให้เบราว์เซอร์ล้างการเปลี่ยนแปลงเค้าโครงที่ค้างอยู่หรือไม่

ตัวอย่างการทำงานด้านล่าง:

var element = document.getElementById("theElement");

function animateX(px) {
    element.style.left = px + "px";
}

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    // apply the "transition: none" and "left: Xpx" rule immediately
    flushCss(element);
    // restore animation
    element.style.transition = "";
}

function flushCss(element) {
  // By reading the offsetHeight property, we are forcing
  // the browser to flush the pending CSS changes (which it
  // does to ensure the value obtained is accurate).
  element.offsetHeight;
}
#theElement {
  position: relative;
  left: 200px;
  transition: left 1s;
  width: 50px;
  height: 50px;
  background: red;
}
.wrapper {
  width: 400px;
  height: 100px;
}
<div class="wrapper">
  <div id="theElement"></div>
</div>
<button onclick="animateX(100)">Animate 100</button>
<button onclick="setX(0)">Set 0</button>

person jperezov    schedule 11.01.2016
comment
คุณจำเป็นต้องฟลัชสองครั้งจริง ๆ หรือไม่ เพราะคำตอบของ lex82 ต้องการการหมดเวลาเพียงครั้งเดียวหรือไม่ - person ; 11.01.2016
comment
ไม่ ฉันคิดว่ามันคงจะเข้าใจง่ายกว่าถ้าแสดงฟลัชสองครั้ง แต่ฉันคิดว่ามันจะทำให้สับสนมากขึ้น ฉันได้แก้ไขคำตอบเพื่อสาธิตสิ่งนี้ - person jperezov; 11.01.2016
comment
ตามที่อธิบายไว้ในคำตอบของฉัน คุณไม่ควรคืนค่าภาพเคลื่อนไหวโดยการตั้งค่าอย่างชัดเจนบนองค์ประกอบ คุณจะแทนที่สไตล์ชีท (เช่นเดียวกับที่คุณทำเมื่อระงับภาพเคลื่อนไหว) - person lex82; 11.01.2016
comment
คำตอบที่ดี. เวอร์ชันของฉันน่าจะทำครึ่งหลังใน setTimeout แต่จะดีกว่า - person Katana314; 11.01.2016
comment
วิธีที่น่าสนใจมากในการบังคับให้ใช้คุณสมบัติโดยใช้ offsetHeight - ขอบคุณสำหรับข้อมูลเชิงลึก! - person Nico Knoll; 02.01.2021

วิธีแก้ไขปัญหาอื่นๆ นั้นดี แต่ฉันต้องการเสนอทางเลือกอื่นและอธิบายว่าเกิดอะไรขึ้น

สาเหตุที่ไม่ทำงานตามที่คาดไว้ในตัวอย่างของคุณเป็นเพราะคุณสมบัติ CSS ทั้งหมดมีการเปลี่ยนแปลงและจากนั้น เหตุการณ์การจัดเรียงเบราว์เซอร์ จะถูกทริกเกอร์และการเปลี่ยนแปลงจะเริ่มต้นขึ้น กล่าวอีกนัยหนึ่ง คุณสมบัติ transition ถูกตั้งค่าเป็น none จากนั้น คุณสมบัติ left จะถูกเปลี่ยน จากนั้น คุณสมบัติ transition จะถูกเปลี่ยนกลับเป็น left 1s

หลังจากอัปเดตคุณสมบัติสไตล์ทั้งหมดแล้ว เหตุการณ์การจัดเรียงเบราว์เซอร์จะถูกทริกเกอร์ CSS จะถูกทาสีใหม่ จากนั้นการเปลี่ยนแปลงจะเริ่มต้นขึ้น:

element.style.transition = "none";
element.style.left = px + "px";
element.style.transition = "left 1s";
// The transition starts after all the CSS has been modified and a reflow has been trigged.

มีเหตุผลบางประการที่มันถูกดำเนินการเช่นนี้

เหตุผลหลักคือประสิทธิภาพ แทนที่จะทาสีใหม่ แต่ละองค์ประกอบหลังจากเปลี่ยนคุณสมบัติ CSS เดียว มีประสิทธิภาพมากกว่ามากในการเปลี่ยนแปลงคุณสมบัติทั้งหมดและมีการทาสีใหม่เพียงครั้งเดียว นอกจากนี้ หากคุณกำลังเปลี่ยนคุณสมบัติหลายรายการ คุณคงคาดหวังว่าคุณสมบัติแต่ละรายการจะมีการเปลี่ยนแปลงก่อนที่องค์ประกอบจะถูกเปลี่ยน (ซึ่งเป็นสิ่งที่เกิดขึ้นจริง)

หากคุณต้องการทางเลือกใหม่ที่ไม่เกี่ยวข้องกับการหมดเวลา/ความล่าช้า หรือการบังคับจัดเรียงใหม่ คุณสามารถตั้งค่าคุณสมบัติ transitionDuration เป็น 0s ก่อนตั้งค่า จากนั้นจึงลบสไตล์อินไลน์นี้ออกเมื่อเปลี่ยนองค์ประกอบ

ตัวอย่างเช่น:

var element = document.getElementById("theElement");

function setX(px) {
  element.style.transitionDuration = '0s';
  element.style.left = px + "px";
}

function animateX(px) {
  element.style.transitionDuration = '';
  element.style.left = px + "px";
}
#theElement {
  position: relative;
  width: 100px;
  height: 100px;
  border: 2px solid;
  left: 200px;
  transition: left 1s;
}
<div id="theElement"></div>

<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>

ในทำนองเดียวกัน คุณยังสามารถสลับคลาสบนองค์ประกอบก่อนที่จะเปลี่ยนหรือตั้งค่า:

var element = document.getElementById("theElement");

function setX(px) {
  element.classList.remove('transition');
  element.style.left = px + "px";
}

function animateX(px) {
  element.classList.add('transition');
  element.style.left = px + "px";
}
#theElement {
  position: relative;
  width: 100px;
  height: 100px;
  border: 2px solid;
  left: 200px;
}
#theElement.transition {
  transition: left 1s;
}
<div id="theElement"></div>

<button onclick="animateX(100)">Transition 100</button>
<button onclick="animateX(300)">Transition 300</button>
<button onclick="setX(0)">Set 0</button>
<button onclick="setX(50)">Set 50</button>

ตัวอย่างด้านบนใช้งานได้ แต่ก็ควรชี้ให้เห็นว่าคุณสามารถฟังเหตุการณ์ transitionend และลบคลาสออกได้เมื่อการเปลี่ยนแปลงสิ้นสุดลง:

ตัวอย่างเช่น:

function animateX(px) {
  element.classList.add('transition');
  element.style.left = px + "px";
  element.addEventListener('transitionend', callback);

  function callback() {
    this.classList.remove('transition');
    this.removeEventListener('transitionend', callback);
  }
}
person Josh Crozier    schedule 11.01.2016
comment
คำตอบที่ดี ฉันไม่รู้ว่าคุณสามารถฟังกิจกรรม transitionend ได้ กิจกรรมนี้รองรับเบราว์เซอร์ที่ดีหรือไม่ - person jperezov; 11.01.2016
comment
@jperezov ใช่ มันมีรองรับเบราว์เซอร์ที่ดี รองรับเบราว์เซอร์สมัยใหม่ทั้งหมด แม้ว่าคุณอาจจำเป็นต้องใช้เวอร์ชันนำหน้าสำหรับเบราว์เซอร์รุ่นเก่า เช่น webkitTransitionEnd, msTransitionEnd - person Josh Crozier; 11.01.2016

คุณสามารถทำได้ แต่คุณต้องคืนค่าคุณสมบัติการเปลี่ยนแปลงเก่าโดยหมดเวลา

function setX(px) {
    element.style.transition = "none";
    element.style.left = px + "px";
    setTimeout(function() {
        element.style.transition = "left 1s";
    });
}

JSFiddle

หากคุณไม่ใช้การหมดเวลาเพื่อคืนค่าคุณสมบัติการเปลี่ยนแปลงเก่า เบราว์เซอร์จะไม่มีทางรู้ว่ามีการเปลี่ยนแปลงเลย มันจะไม่แสดงผลหน้าเว้นแต่ว่าโค้ดของคุณจะทำงานอย่างสมบูรณ์

ฉันยังแนะนำให้ยกเลิกการตั้งค่าคุณสมบัติการเปลี่ยนแปลงดังนี้:

element.style.transition = "";

มิฉะนั้น คุณจะแทนที่สไตล์ชีตถาวรสำหรับองค์ประกอบนี้ ก่อนที่คุณจะตั้งค่าลักษณะการเปลี่ยนผ่านในองค์ประกอบ การเปลี่ยนจะถูกกำหนดโดยสไตล์ชีต หากคุณตั้งค่ารูปแบบการเปลี่ยนบนองค์ประกอบ คุณจะแทนที่สไตล์ชีต

อัปเดต: ดูเหมือนว่าการหมดเวลาจะไม่เพียงพอ ฉันลองใน FF/Linux แล้ว แต่มันไม่ทำงาน ดังนั้นคุณอาจเพิ่มอีกมิลลิวินาที (ไม่แนะนำ) หรือใช้วิธีแก้ปัญหาของ @jperezov (แนะนำ)

person lex82    schedule 11.01.2016
comment
นอกจากนี้ setTimeout ไม่ต้องการอาร์กิวเมนต์ที่สองเป็นมิลลิวินาทีใช่หรือไม่ - person ; 11.01.2016
comment
มันไม่ได้ ค่าเริ่มต้นคือศูนย์ ดังนั้นจึงแทบไม่มีความล่าช้าเลย - person lex82; 11.01.2016
comment
คุณหมายถึงอะไรโดยการแทนที่สไตล์ชีทตลอดไป คุณช่วยยกตัวอย่างได้ไหม? - person ; 11.01.2016
comment
ไม่ใช่ความคิดที่ดีที่จะแนะนำ setTimeout โดยไม่รู้ว่าเกิดอะไรขึ้นเบื้องหลัง ซึ่งอธิบายได้ดีกว่าในคำตอบของ Josh Crozier - person Jan Turoň; 28.10.2020

เพียงเพื่อเพิ่มบางส่วนให้เพียงพอและอธิบายคำตอบของ Josh Crozier ได้ดี:

นอกจากนี้ยังมีเหตุการณ์ ontransitioncalcel ซึ่งจะเกิดขึ้นเมื่อเหตุการณ์เกิดขึ้น ถูกยกเลิก ดังนั้นคุณสามารถทดสอบได้ว่าบางวิธียกเลิกหรือไม่ จากฉบับร่างปัจจุบัน ฉันเลือกเอง:

หากองค์ประกอบไม่อยู่ในเอกสารอีกต่อไป การใช้งานจะต้องยกเลิกการเปลี่ยนที่กำลังทำงานอยู่

เพื่อให้คุณสามารถลบองค์ประกอบ ทำการเปลี่ยนแปลง และเพิ่มองค์ประกอบกลับไปยังตำแหน่งเดิม การตั้งค่า display: none มีผลข้างเคียงมากมาย หยุดการเปลี่ยนแปลงระหว่างกัน

หากองค์ประกอบมีการเปลี่ยนแปลงที่ทำงานอยู่สำหรับคุณสมบัติ และไม่มีค่าคุณสมบัติการเปลี่ยนแปลงที่ตรงกัน การใช้งานจะต้องยกเลิกการเปลี่ยนแปลงที่ทำงานอยู่

เพื่อให้คุณสามารถพยายามลบค่าคุณสมบัติการเปลี่ยนแปลง

หากค่าปัจจุบันของคุณสมบัติในการเปลี่ยนแปลงที่กำลังทำงานอยู่เท่ากับมูลค่าของคุณสมบัติในรูปแบบหลังการเปลี่ยนแปลง หรือหากค่าทั้งสองนี้ไม่สามารถเปลี่ยนผ่านได้ การใช้งานจะต้องยกเลิกการเปลี่ยนแปลงที่กำลังทำงานอยู่

เพื่อให้คุณสามารถตั้งค่าคุณสมบัติด้านซ้ายเป็น auto หรือ initial ก่อนเพื่อหยุดการเปลี่ยนแปลง

อนิจจา ไม่มีกิจกรรมการเปลี่ยนแปลงใดที่สามารถยกเลิกได้ ดังนั้นการเรียก e.preventDefault() ในกิจกรรมเหล่านั้นจึงไม่ควรหยุดกิจกรรมเหล่านั้น

person Jan Turoň    schedule 28.10.2020