สรุปคร่าวๆ
ใช้การแปลงอัตราส่วนเมื่อทำให้คลิปเคลื่อนไหว คุณสามารถป้องกันไม่ให้เด็กยืดและบิดในขณะที่เคลื่อนไหวได้โดยการปรับขนาดตัวนับ
ก่อนหน้านี้เราโพสต์อัปเดตเกี่ยวกับวิธีสร้างเอฟเฟกต์พารัลแลกซ์และการเลื่อนได้ไม่รู้จบที่มีประสิทธิภาพ ในโพสต์นี้ เราจะมาดูสิ่งที่เกี่ยวข้องหากคุณต้องการภาพเคลื่อนไหวของคลิปที่มีประสิทธิภาพ หากคุณต้องการดูการสาธิต โปรดดูตัวอย่างที่เก็บ GitHub สำหรับองค์ประกอบ UI
ตัวอย่างเช่น เมนูแบบขยาย
ตัวเลือกบางอย่างในการสร้างโฆษณานี้มีประสิทธิภาพมากกว่าตัวเลือกอื่นๆ
ไม่ดี: ภาพเคลื่อนไหวความกว้างและความสูงในองค์ประกอบคอนเทนเนอร์
คุณสามารถใช้ CSS สั้นๆ เพื่อทำให้ความกว้างและความสูงในองค์ประกอบคอนเทนเนอร์เคลื่อนไหวได้
.menu {
overflow: hidden;
width: 350px;
height: 600px;
transition: width 600ms ease-out, height 600ms ease-out;
}
.menu--collapsed {
width: 200px;
height: 60px;
}
ปัญหาเร่งด่วนของวิธีนี้คือการทำให้ width
และ height
เคลื่อนไหว
คุณสมบัติเหล่านี้ต้องมีการคำนวณเลย์เอาต์และระบายสีผลลัพธ์ในทุกเฟรมของภาพเคลื่อนไหว
ซึ่งอาจมีค่าใช้จ่ายสูงและมักทำให้คุณพลาดการใช้ 60 FPS ถ้าเป็นข่าวสำหรับคุณ ให้อ่านคู่มือประสิทธิภาพการแสดงผลของเรา ซึ่งจะดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของกระบวนการแสดงผล
ไม่ดี: ใช้พร็อพเพอร์ตี้คลิป CSS หรือคลิปเส้นทาง
อีกทางเลือกหนึ่งที่ใช้แทนการสร้าง width
และ height
ให้เป็นภาพเคลื่อนไหวคือการใช้พร็อพเพอร์ตี้ clip
(เลิกใช้งานแล้ว) เพื่อสร้างการเคลื่อนไหวให้กับเอฟเฟกต์ขยายและยุบ หรือจะใช้ clip-path
แทนก็ได้หากต้องการ อย่างไรก็ตาม การใช้ clip-path
ได้รับการรองรับน้อยกว่า clip
แต่ clip
เลิกใช้งานแล้ว ทางขวา แต่อย่าเพิ่งสิ้นหวัง นี่ไม่ใช่วิธีแก้ปัญหาที่คุณต้องการ
.menu {
position: absolute;
clip: rect(0px 112px 175px 0px);
transition: clip 600ms ease-out;
}
.menu--collapsed {
clip: rect(0px 70px 34px 0px);
}
แม้ว่าจะทำให้ width
และ height
ขององค์ประกอบเมนูเคลื่อนไหวได้ดีกว่า แต่ข้อเสียของวิธีนี้คือยังคงทริกเกอร์ Paint นอกจากนี้ หากคุณใช้พร็อพเพอร์ตี้ clip
ตามเส้นทางดังกล่าว องค์ประกอบที่กําลังทํางานอยู่จะต้องอยู่ในตําแหน่งที่แน่นอนหรือคงที่ ซึ่งอาจต้องใช้การลองผิดลองถูกเล็กน้อย
ดี: แสดงสเกลภาพเคลื่อนไหว
เนื่องจากเอฟเฟกต์นี้จะมีบางอย่างที่ใหญ่ขึ้นและเล็กลง คุณจึงใช้การเปลี่ยนรูปแบบได้ นี่เป็นข่าวดี เพราะการเปลี่ยนแปลงของการเปลี่ยนรูปแบบเป็นสิ่งที่ไม่จำเป็นต้องใช้การจัดวางหรือลงสี และเบราว์เซอร์สามารถส่งผ่าน GPU ได้ ซึ่งหมายความว่าเอฟเฟ็กต์จะถูกเร่งขึ้นและมีแนวโน้มที่จะถึง 60 เฟรมต่อวินาทีเป็นอย่างมาก
ข้อเสียของวิธีนี้ก็เหมือนกับประสิทธิภาพการแสดงผลส่วนใหญ่ คือต้องมีการตั้งค่าเล็กน้อย แต่รับรองว่าคุ้มค่าแน่นอน
ขั้นตอนที่ 1: คำนวณสถานะเริ่มต้นและสิ้นสุด
สำหรับวิธีใช้ภาพเคลื่อนไหวแบบปรับขนาด ขั้นตอนแรกคือการอ่านองค์ประกอบที่บอกขนาดที่เมนูจะต้องอยู่ทั้งเมื่อยุบและเมื่อขยาย อาจเป็นเพราะในบางสถานการณ์ คุณไม่ได้รับข้อมูลทั้ง 2 ชิ้นในคราวเดียวกัน จึงต้องพูดว่าสลับคลาสเพื่อให้อ่านสถานะต่างๆ ของคอมโพเนนต์ได้
อย่างไรก็ตาม หากต้องการให้เป็นแบบนั้น โปรดใช้ความระมัดระวัง: getBoundingClientRect()
(หรือ offsetWidth
และ offsetHeight
) จะบังคับให้เบราว์เซอร์เรียกใช้รูปแบบและเลย์เอาต์ที่ส่งหากรูปแบบมีการเปลี่ยนแปลงตั้งแต่เรียกใช้ครั้งล่าสุด
function calculateCollapsedScale () {
// The menu title can act as the marker for the collapsed state.
const collapsed = menuTitle.getBoundingClientRect();
// Whereas the menu as a whole (title plus items) can act as
// a proxy for the expanded state.
const expanded = menu.getBoundingClientRect();
return {
x: collapsed.width / expanded.width,
y: collapsed.height / expanded.height
};
}
ในกรณีอย่างเมนู เราสามารถตั้งสมมติฐานอย่างสมเหตุสมผลว่าเมนูจะเริ่มเป็นขนาดปกติ (1, 1) สเกลแบบธรรมชาตินี้แสดงถึงสถานะที่ขยายแล้ว ซึ่งหมายความว่าคุณจะต้องสร้างภาพเคลื่อนไหวจากเวอร์ชันที่ปรับขนาดลง (ซึ่งคำนวณไว้ข้างต้น) แล้วกลับไปขนาดที่เป็นธรรมชาติดังกล่าว
แต่เดี๋ยวก่อน จริงๆ แล้ววิธีนี้จะช่วยปรับขนาดเนื้อหาของเมนูด้วย ว่าไหม อย่างที่เห็นด้านล่างนี้ ก็ใช้ได้แล้วครับ
แล้วคุณทำอะไรได้บ้าง อcounter- มี 2 สิ่งที่ควรสังเกตเกี่ยวกับเรื่องดังกล่าว ได้แก่
การเปลี่ยนรูปแบบตัวนับยังเป็นการดําเนินการเกี่ยวกับขนาดด้วย นี่เป็นดีเนื่องจากสามารถเร่งความเร็วได้ เช่นเดียวกับภาพเคลื่อนไหวในคอนเทนเนอร์ คุณอาจต้องตรวจสอบว่าองค์ประกอบที่เป็นภาพเคลื่อนไหวมีเลเยอร์องค์ประกอบเอง (ช่วยให้ GPU สามารถช่วยได้) และให้คุณสามารถเพิ่ม
will-change: transform
ลงในองค์ประกอบหรือbackface-visiblity: hidden
หากคุณจำเป็นต้องรองรับเบราว์เซอร์รุ่นเก่าต้องคำนวณการเปลี่ยนรูปแบบตัวนับต่อเฟรม จากตรงนี้อาจมีเรื่องยุ่งยากเล็กน้อย เนื่องจากภาพเคลื่อนไหวใน CSS และใช้ฟังก์ชันการค่อยๆ เปลี่ยน จะต้องตอบโต้การค่อยๆ เปลี่ยนเมื่อทำให้ภาพเคลื่อนไหวของการเปลี่ยนรูปแบบเคลื่อนไหว แต่การคำนวณเส้นโค้งผกผัน เช่น
cubic-bezier(0, 0, 0.3, 1)
ไม่ใช่สิ่งที่ชัดเจนขนาดนั้น
ดังนั้น คุณอาจจะอยากลองทำให้เอฟเฟ็กต์เคลื่อนไหวโดยใช้ JavaScript จากนั้นก็ใช้สมการการค่อยๆ เปลี่ยนเพื่อคำนวณค่าสเกลและค่าตัวนับต่อเฟรม ข้อเสียของภาพเคลื่อนไหวที่ใช้ JavaScript คือสิ่งที่เกิดขึ้นเมื่อเทรดหลัก (ที่ JavaScript ทำงาน) ยุ่งอยู่กับงานอื่น คำตอบสั้นๆ คือภาพเคลื่อนไหวอาจกระตุกหรือหยุดไปเลย ซึ่งไม่ดีต่อ UX
ขั้นตอนที่ 2: สร้างภาพเคลื่อนไหว CSS อย่างรวดเร็ว
วิธีแก้ไขซึ่งอาจดูแปลกๆ ในตอนแรกคือการสร้างภาพเคลื่อนไหวที่มีคีย์เฟรมด้วยฟังก์ชันการค่อยๆ เปลี่ยนของเราเองแบบไดนามิก และแทรกภาพนั้นลงในหน้าเพื่อใช้โดยเมนู (ต้องขอขอบคุณวิศวกร Chrome Robert Flack ที่ชี้ให้เห็นข้อมูลนี้) ประโยชน์หลักของวิธีนี้คือภาพเคลื่อนไหวที่มีคีย์เฟรมที่เปลี่ยนแปลงการเปลี่ยนรูปแบบสามารถเรียกใช้บนตัวจัดวางองค์ประกอบได้ ซึ่งหมายความว่าวิดีโอนั้นจะไม่ได้รับผลจากงานในเทรดหลัก
ในการสร้างภาพเคลื่อนไหวของคีย์เฟรม เราจะกำหนดระดับจาก 0 ถึง 100 และคำนวณค่าสเกลที่ต้องใช้สำหรับองค์ประกอบและเนื้อหา จากนั้นจะสรุปให้เป็นสตริง ซึ่งสามารถแทรกลงในหน้าเว็บเป็นองค์ประกอบรูปแบบได้ การแทรกรูปแบบจะทำให้มีการส่งรูปแบบ "คำนวณสไตล์ใหม่" ไปยังหน้า ซึ่งเป็นงานที่เบราว์เซอร์ต้องทำเพิ่มเติม แต่จะทำเฉพาะเมื่อคอมโพเนนต์เริ่มทำงานเท่านั้น
function createKeyframeAnimation () {
// Figure out the size of the element when collapsed.
let {x, y} = calculateCollapsedScale();
let animation = '';
let inverseAnimation = '';
for (let step = 0; step <= 100; step++) {
// Remap the step value to an eased one.
let easedStep = ease(step / 100);
// Calculate the scale of the element.
const xScale = x + (1 - x) * easedStep;
const yScale = y + (1 - y) * easedStep;
animation += `${step}% {
transform: scale(${xScale}, ${yScale});
}`;
// And now the inverse for the contents.
const invXScale = 1 / xScale;
const invYScale = 1 / yScale;
inverseAnimation += `${step}% {
transform: scale(${invXScale}, ${invYScale});
}`;
}
return `
@keyframes menuAnimation {
${animation}
}
@keyframes menuContentsAnimation {
${inverseAnimation}
}`;
}
สงสัยไม่รู้จบอาจสงสัยเกี่ยวกับฟังก์ชัน ease()
ภายใน For-loop คุณใช้ลักษณะนี้เพื่อแมปค่าจาก 0 ถึง 1 ไปยังค่าเทียบเท่าแบบค่อยๆ เปลี่ยนได้
function ease (v, pow=4) {
return 1 - Math.pow(1 - v, pow);
}
นอกจากนี้ คุณยังใช้ Google Search เพื่อพล็อตหน้าตา ได้ด้วย สะดวก หากคุณต้องการสมการการค่อยๆ เปลี่ยนแบบอื่นๆ ลองดู Tween.js โดย Soledad Penadés ที่มีสมการจำนวนมาก
ขั้นตอนที่ 3: เปิดใช้ภาพเคลื่อนไหว CSS
ด้วยการสร้างภาพเคลื่อนไหวเหล่านี้และเพิ่มลงในหน้าเว็บด้วย JavaScript ขั้นตอนสุดท้ายคือสลับชั้นเรียนที่เปิดใช้ภาพเคลื่อนไหว
.menu--expanded {
animation-name: menuAnimation;
animation-duration: 0.2s;
animation-timing-function: linear;
}
.menu__contents--expanded {
animation-name: menuContentsAnimation;
animation-duration: 0.2s;
animation-timing-function: linear;
}
การดำเนินการนี้จะทำให้ภาพเคลื่อนไหวที่สร้างขึ้นในขั้นตอนก่อนหน้าทำงาน เนื่องจากภาพเคลื่อนไหวที่ผ่อนปรนขึ้นแล้ว จึงต้องตั้งค่าฟังก์ชันการจับเวลาเป็น linear
มิเช่นนั้นคุณจะสลับไปมาระหว่างแต่ละคีย์เฟรมได้ ซึ่งจะดูแปลกๆ มาก
เมื่อกล่าวถึงการยุบองค์ประกอบกลับลงมา จะมี 2 ตัวเลือก คือ อัปเดตภาพเคลื่อนไหว CSS ให้แสดงผลแบบย้อนกลับแทนไปข้างหน้า แม้จะใช้ได้ดี แต่ "ความรู้สึก" ของภาพเคลื่อนไหวจะกลับกัน ดังนั้นหากคุณใช้เส้นโค้งในการค่อยๆ เปลี่ยน การกลับด้านจะค่อยๆ ลดลงเข้า ซึ่งจะทำให้รู้สึกเอื่อยๆ วิธีแก้ปัญหาที่เหมาะสมกว่าคือการสร้างภาพเคลื่อนไหวคู่ที่ 2 สำหรับการยุบองค์ประกอบ ภาพเคลื่อนไหวของคีย์เฟรมเหล่านี้สามารถสร้างได้ในวิธีเดียวกับการขยาย ภาพเคลื่อนไหวของคีย์เฟรม แต่สลับค่าเริ่มต้นและสิ้นสุด
const xScale = 1 + (x - 1) * easedStep;
const yScale = 1 + (y - 1) * easedStep;
เวอร์ชันขั้นสูงกว่า: แสดงแบบวงกลม
คุณยังสามารถใช้เทคนิคนี้เพื่อสร้างภาพเคลื่อนไหวแบบขยายและยุบแบบวงกลมได้ด้วย
หลักการส่วนใหญ่แล้วจะเหมือนกับเวอร์ชันก่อนหน้า นั่นคือให้คุณปรับขนาดองค์ประกอบและปรับขนาดให้สอดคล้อง ในกรณีนี้ องค์ประกอบที่ปรับขนาดขึ้นจะมี border-radius
อยู่ที่ 50% ทำให้เป็นวงกลม และล้อมรอบด้วยองค์ประกอบอื่นที่มี overflow: hidden
ซึ่งหมายความว่าคุณจะไม่เห็นวงกลมขยายออกนอกขอบเขตขององค์ประกอบ
คำเตือนสำหรับตัวแปรนี้โดยเฉพาะคือ Chrome มีข้อความเบลอบนหน้าจอที่มี DPI ต่ำระหว่างภาพเคลื่อนไหว เนื่องจากมีข้อผิดพลาดในการปัดเศษเนื่องจากขนาดและข้อความ หากสนใจเกี่ยวกับรายละเอียดดังกล่าว เรามีรายงานข้อบกพร่องให้คุณติดดาวและติดตามได้
ดูโค้ดสำหรับเอฟเฟกต์ขยายแบบวงกลมได้ในที่เก็บของ GitHub
บทสรุป
เท่านี้ก็เรียบร้อย วิธีสร้างคลิปเคลื่อนไหวที่มีประสิทธิภาพโดยใช้การแปลงอัตราส่วน ในโลกที่ดีมาก คงจะดีไม่น้อยถ้าเราเร่งภาพเคลื่อนไหวของคลิปได้ (มีข้อบกพร่องของ Chromium ที่ Jake Archibald สร้างขึ้น) แต่จนกว่าเราจะไปถึงจุดนั้น คุณควรระมัดระวังเมื่อทำให้ clip
หรือ clip-path
เคลื่อนไหว และหลีกเลี่ยงการทำให้ width
หรือ height
เคลื่อนไหว
การใช้ภาพเคลื่อนไหวบนเว็บสำหรับเอฟเฟ็กต์แบบนี้ก็อาจเป็นประโยชน์เช่นกัน เนื่องจากมี JavaScript API แต่ทำงานในชุดองค์ประกอบได้หากคุณทำให้ transform
และ opacity
เคลื่อนไหวเท่านั้น
แต่ขออภัยที่การรองรับภาพเคลื่อนไหวบนเว็บยังไม่ค่อยดีนัก
แต่คุณใช้การเพิ่มประสิทธิภาพแบบต่อเนื่องได้หากมีให้บริการ
if ('animate' in HTMLElement.prototype) {
// Animate with Web Animations.
} else {
// Fall back to generated CSS Animations or JS.
}
ในระหว่างนี้ คุณสามารถใช้ไลบรารีแบบ JavaScript ในการสร้างภาพเคลื่อนไหว แต่คุณอาจพบว่ามีประสิทธิภาพน่าเชื่อถือมากขึ้นด้วยการ Bake ภาพเคลื่อนไหว CSS แล้วใช้รูปแบบนั้นแทน ในทำนองเดียวกัน หากแอปใช้ JavaScript สำหรับภาพเคลื่อนไหวของแอปอยู่แล้ว แอปของคุณอาจให้บริการได้ดียิ่งขึ้นเนื่องจากมีความสอดคล้องกับฐานของโค้ดที่คุณมีอยู่เป็นอย่างน้อย
หากต้องการดูรายละเอียดโค้ดสำหรับเอฟเฟกต์นี้ โปรดดูตัวอย่างที่เก็บ GitHub สำหรับองค์ประกอบ UI และเช่นเคย โปรดแจ้งให้เราทราบถึงวิธีการใช้งานของคุณในส่วนความคิดเห็นด้านล่าง