Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Javascript

สร้างแอปขนาดเล็ก "รับวิวพอร์ต" ที่มีประสิทธิภาพด้วย JavaScript

บทช่วยสอน Vanilla JavaScript ที่เป็นมิตรกับผู้เริ่มต้นใช้งาน ประเด็นสำคัญ:วิธีเขียน เหตุการณ์หน้าต่างประสิทธิภาพสูง ที่จะไม่ทำลายฮาร์ดแวร์ของผู้ใช้ของเรา

สำหรับ:สมบูรณ์ระดับเริ่มต้นจนถึงระดับกลาง

วันนี้ เรากำลังสร้างแอปเล็กๆ ที่เรียกว่า “ขนาดวิวพอร์ต” คุณลักษณะนี้ทำสิ่งหนึ่งได้ดีมาก:แสดงค่าความกว้างและความสูงของวิวพอร์ตของเบราว์เซอร์เมื่อคุณปรับขนาดหน้าต่างเบราว์เซอร์ของคุณ

นี่เป็นเครื่องมือที่ทรงคุณค่าเมื่อคุณทำการออกแบบที่ตอบสนอง CodePen มีคุณสมบัติที่คล้ายกัน แต่ฉันต้องการสิ่งที่ใหญ่กว่านี้และคุณสมบัติพิเศษบางอย่าง ดังนั้นฉันจึงตัดสินใจสร้างของตัวเอง ฉันได้เรียนรู้มากมายในกระบวนการนี้ โดยเฉพาะอย่างยิ่งเกี่ยวกับวิธีจัดการกับปัญหาด้านประสิทธิภาพ ตอนนี้ฉันกำลังแบ่งปันกับคุณ!

ขนาดวิวพอร์ต

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

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

หัวข้อทางเทคนิค:

  • วิธีสร้างองค์ประกอบด้วย createElement() วิธีการ
  • วิธีการจัดรูปแบบองค์ประกอบด้วย Style object
  • วิธีตั้งค่าและใช้งาน resize . ของ Window Object เหตุการณ์
  • วิธีรับความกว้างและความสูงจากวิวพอร์ตของเบราว์เซอร์ด้วย innerWidth และ innerHeight คุณสมบัติ
  • วิธีเรียกใช้ฟังก์ชันนอกฟังก์ชัน (เรียกกลับ)
  • มีเงื่อนไขอย่างไร (จริง/เท็จ)if งบทำงาน.
  • วิธีชะลอการเรียกใช้โค้ดด้วยตัวจับเวลา โดยใช้setTimeout()
  • วิธีรีเซ็ตตัวจับเวลาด้วย clearTimeout() .
  • วิธีใช้ฟังก์ชันตัวควบคุมเหตุการณ์ เพื่อปรับปรุงประสิทธิภาพของเหตุการณ์หน้าต่างอย่างมาก
  • ความสัมพันธ์ระหว่าง MS &FPS (มิลลิวินาทีและเฟรมต่อวินาที)
  • ความแตกต่างที่สำคัญระหว่าง การรับรู้ประสิทธิภาพ และ ประสิทธิภาพของฮาร์ดแวร์

แนวทาง

เรากำลังสร้างแอป Viewport Dimensions 2 เวอร์ชัน

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

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

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

วิธีการเรียนรู้

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

หากคุณหลงทาง คุณสามารถอ้างอิงโค้ดที่เสร็จแล้วได้เสมอ:

  • เวอร์ชัน 1
  • เวอร์ชัน 2

เริ่มกันเลย!

ขนาดวิวพอร์ต [เวอร์ชัน 1.0]

ก่อนที่เราจะเริ่มเขียนโค้ด มาดูข้อกำหนดของโครงการกันก่อน

ข้อกำหนด

  • ควรมีคอนเทนเนอร์สำหรับแสดงความกว้างและความสูงของหน้าต่าง
  • ควรอยู่ที่มุมขวาบนของหน้าต่าง
  • ควรซ่อนไว้โดยค่าเริ่มต้น
  • ควรปรากฏขึ้นทันทีที่ปรับขนาดหน้าต่าง
  • ควรซ่อนหลังจากปรับขนาดหน้าต่างล่าสุด 3 วินาที

นั่นคือสาระสำคัญของข้อกำหนดคุณลักษณะสำหรับเวอร์ชัน 1

มาเริ่มเขียนโค้ดกันเลย!

สร้างคอนเทนเนอร์ดิสเพลย์และจัดสไตล์

ขั้นแรก มาสร้างองค์ประกอบ div สำหรับแอป Viewport Dimensions โดยใช้ createElement เมธอดและกำหนดให้กับตัวแปร:

var viewportDimensions = document.createElement("div");

ตอนนี้เรามี <div></div> . ว่างแล้ว องค์ประกอบที่เก็บไว้ในตัวแปร

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

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

viewportDimensions.style.position = "fixed";
viewportDimensions.style.right = "0";
viewportDimensions.style.top = "0";
viewportDimensions.style.padding = "16px";
viewportDimensions.style.zIndex = "3";
viewportDimensions.style.fontSize = "22px";

ไวยากรณ์ด้านบนคือ เครื่องหมายจุด พูดง่ายๆ คือ เราใช้ . เพื่อเข้าถึงคุณสมบัติหรือวิธีการของวัตถุ ในตัวอย่างข้างต้น เรากำลังพูดว่า:

  • สวัสดี viewportDimensions
  • ให้สิทธิ์ฉันเข้าถึง Style Object
  • ดังนั้นฉันจึงสามารถเข้าถึงคุณสมบัติการจัดรูปแบบ

การจัดสไตล์ด้วยอ็อบเจกต์ Style ก็เหมือนกับการจัดสไตล์ด้วย CSS แต่ออบเจ็กต์ Style ใช้ สไตล์อินไลน์ คุณสมบัติการกำหนดสไตล์จะเพิ่มไปยังองค์ประกอบ HTML โดยตรง ไม่ใช่ในสไตล์ชีต CSS ภายนอก

มายืนยันว่าโค้ดของเราใช้งานได้โดยต่อท้าย Viewport Dimensions <div> องค์ประกอบไปยัง <body> องค์ประกอบของเอกสาร HTML ของเรา

ขั้นแรก ให้ประกาศตัวแปรเพื่อเก็บข้อมูลอ้างอิงไปยังองค์ประกอบเนื้อหาในเอกสารของเรา เพิ่มโค้ดต่อไปนี้ที่ด้านบนของไฟล์ JavaScript

var body = document.querySelector("body");

ตอนนี้ มาเพิ่มองค์ประกอบ div ของ Viewport Dimension ภายในองค์ประกอบเนื้อหากัน ใส่รหัสต่อไปนี้ที่ด้านล่างของไฟล์ JavaScript ของคุณ (ใต้รูปแบบสุดท้าย:

body.appendChild(viewportDimensions);

appendChild() เป็น วิธีการ ที่ผนวก (เพิ่ม) โหนดภายในโหนดอื่น ทุกอย่าง ใน DOM เป็นโหนดและมีโหนดประเภทต่างๆ ในกรณีนี้ เรากำลังเพิ่มโหนดองค์ประกอบ div ภายในโหนดองค์ประกอบร่างกาย

เนื่องจากองค์ประกอบ div ของเรายังไม่มีเนื้อหาใดๆ เราจึงไม่สามารถดูได้ว่าโค้ดปัจจุบันของเราใช้งานได้หรือไม่ เราสามารถค้นหาได้อย่างรวดเร็วโดยการเพิ่มเส้นขอบชั่วคราวให้กับองค์ประกอบ:

viewportDimensions.style.border = "10px solid";

หากใช้งานได้ ตอนนี้คุณจะเห็นกล่องเล็กๆ ที่มีเส้นขอบสีดำหนาที่มุมบนขวาของหน้าต่าง:

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

คุณสามารถตรวจสอบได้ด้วยการคลิกขวาในหน้าต่างเบราว์เซอร์ของคุณ จากนั้นคลิกที่ ตรวจสอบ เพื่อเข้าถึงเบราว์เซอร์ DevTools

ตอนนี้ภายใต้แท็บองค์ประกอบ ค้นหา body . ของคุณ องค์ประกอบ และคุณควรเห็นองค์ประกอบ div ที่ว่างเปล่า พร้อมคุณสมบัติสไตล์อินไลน์บางส่วน

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript สังเกตว่า div ที่มองไม่เห็นจะถูกเน้นที่มุมบนขวาเมื่อคุณเลื่อนเมาส์ไปบน DevTools

นี่คือภาพรวมของโค้ดทั้งหมดของเรา:

// Select body element
var body = document.querySelector("body");

// Create div element
var viewportDimensions = document.createElement("div");

// Style element
viewportDimensions.style.position = "fixed";
viewportDimensions.style.right = "0";
viewportDimensions.style.top = "0";
viewportDimensions.style.padding = "16px";
viewportDimensions.style.zIndex = "3";
viewportDimensions.style.fontSize = "22px";

// Add div element inside body element
body.appendChild(viewportDimensions);

เยี่ยมมาก องค์ประกอบ div ของเราได้รับการสร้าง จัดรูปแบบ และผนวกไว้ในองค์ประกอบร่างกายของเรา ต่อไปเป็นกิจกรรมปรับขนาดหน้าต่าง

การตั้งค่าเหตุการณ์การปรับขนาดหน้าต่าง

Window Object แสดงถึงหน้าต่างเบราว์เซอร์ของคุณ มีหลายวิธี ได้แก่ addEventListener . วิธีนี้สามารถฟัง ประเภท ได้มากมาย ของเหตุการณ์ของหน้าต่าง เช่น การเลื่อน การคลิก การพิมพ์ และการปรับขนาด

เราจะใช้ 'resize' เหตุการณ์. ด้านล่าง body.appendChild เพิ่มสิ่งต่อไปนี้:

window.addEventListener("resize", function() {
  // Code to execute when window resizes
});

หมายเหตุ:ความคิดเห็น // ไม่ได้ถูกตีความโดยเบราว์เซอร์ แต่มีไว้สำหรับมนุษย์เท่านั้น

ก่อนหน้านี้เราใช้เครื่องหมายจุดเพื่อดึงคุณสมบัติขององค์ประกอบ div ตอนนี้เราใช้มันเพื่อเข้าถึง วิธีการ บนวัตถุหน้าต่าง:addEventListener .

เรากำลังส่งข้อโต้แย้งสองข้อ ให้กับผู้ฟังเหตุการณ์:'resize' และ function() . เราได้พูดคุยกันถึงกิจกรรมการปรับขนาดแล้ว แต่มีไว้เพื่ออะไร

function() ตั้งค่าบล็อคโค้ดที่เราต้องการดำเนินการทุกครั้งที่มีการเปิดใช้งานเหตุการณ์การปรับขนาดหน้าต่าง (ทันทีที่คุณปรับขนาดหน้าต่างของคุณ) โค้ดบล็อกคือสิ่งที่อยู่ในวงเล็บปีกกา { } (เรียกอีกอย่างว่า ตัวฟังก์ชัน) ซึ่งจนถึงตอนนี้มีเพียง // comment .

ทุกครั้งที่เราเรียกเหตุการณ์หน้าต่าง มันจะบอก function() เพื่อเรียกใช้รหัสภายในวงเล็บปีกกา { } .

เพื่อยืนยันว่าใช้งานได้ ให้ทดสอบโดยใช้ console.log() :

window.addEventListener("resize", function() {
  // Code to execute when window resizes
  console.log("Working!");
});

ตอนนี้ปรับขนาดหน้าต่างของคุณ หากคอนโซลของคุณพิมพ์ว่า 'กำลังทำงาน!' หลายครั้ง หมายความว่ากิจกรรมการปรับขนาดหน้าต่างของคุณกำลังทำงาน

การเข้าถึงคอนโซล

ในการเข้าถึงคอนโซลใน CodePen ให้คลิกปุ่มคอนโซลที่มุมล่างซ้ายของหน้าต่าง ในการเข้าถึงคอนโซลใน Chrome นี่คือแป้นพิมพ์ลัด:

  • Cmd + Opt + J (Mac)
  • Ctrl + Shift + J (Windows / Linux)

เอาล่ะ มาทำให้ Window Event ของเราทำงานกันเถอะ!

แสดงความกว้างและความสูงของเบราว์เซอร์เป็นพิกเซล

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

คาดเดาสิ่งที่พวกเขาทำ?

ใช่ มันจะคืนค่าความกว้างและความสูงของหน้าต่างเบราว์เซอร์ของคุณเป็นพิกเซล (รวมถึงแถบเลื่อน) มายืนยันและใช้ console.log() เพื่อคืนค่าคุณสมบัติความกว้างและความสูงเหล่านั้น

เนื่องจากเราต้องการดึงค่าปัจจุบัน ไดนามิก เราต้องใส่ console.log() . ของเรา อยู่ในบล็อกฟังก์ชันของเหตุการณ์ปรับขนาด:

window.addEventListener("resize", function() {
  // Code to execute when window resizes
  console.log("Width:", window.innerWidth);
  console.log("Height:", window.innerHeight);
});

'Width:' และ 'Height:' ข้อความใน console.log() มีไว้เพื่อให้เอาต์พุตคอนโซลชัดเจน

จริงๆ แล้วเราไม่ต้องเขียน window. ในการคว้าค่า width &height เพียงแค่ innerWidth และ innerHeight จะทำงาน. ฉันทำเพื่อหลีกเลี่ยงความสับสน

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

ควรมีลักษณะดังนี้:

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

ตอนนี้เรากำลังจะไปที่ไหนสักแห่ง!

มากำจัด console.log() . กันเถอะ และกำหนดคุณสมบัติความกว้างและความสูงให้กับตัวแปรของตัวเองแทน เพื่อให้เราสามารถเก็บข้อมูลอ้างอิงได้:

window.addEventListener("resize", function() {
  // Code to execute when window resizes
  var width = window.innerWidth;
  var height = window.innerHeight;
});

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

เพื่อยืนยันว่าตัวแปรใหม่ของคุณทำงานตามที่ควรจะเป็น ให้ลองพิมพ์ตัวแปรใหม่เหล่านั้นไปที่คอนโซล:

window.addEventListener("resize", function() {
  // Code to execute when window resizes
  var width = window.innerWidth;
  var height = window.innerHeight;

  console.log(width);
  console.log(height);
});

หากทุกอย่างใช้งานได้ คุณสามารถลบ console.log ออกแล้วไปต่อได้

การเพิ่มค่าความกว้างและความสูงให้กับองค์ประกอบ div

เราต้องการเพิ่มค่าความกว้างและความสูงที่ส่งคืนให้กับองค์ประกอบ div ของเรา โดยเฉพาะอย่างยิ่งเราต้องการฝังค่า ภายใน แท็กเปิดและปิดองค์ประกอบ div ของเรา:

&lt;div&gt; We want width &amp; height values here &lt;/div&gt;

ในการทำเช่นนั้น เราจำเป็นต้องคว้าตัวแปรที่เก็บองค์ประกอบ div ของเรา:viewportDimensions . จากนั้นเราต้องเข้าถึงคุณสมบัติบนวัตถุองค์ประกอบที่เรียกว่า textContent . เราใช้เครื่องหมายจุดแบบเดิม:

viewportDimensions.textContent;

ตอนนี้เราเพียงแค่ต้องกำหนดตัวแปรความกว้างและความสูงของเรา:

viewportDimensions.textContent = width + "px" + " x " + height + "px";

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

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

ฉันรู้ว่ารูปแบบโค้ดที่เราใช้ด้านบนอาจดูสับสนเล็กน้อย

มาทำลายมันกันเถอะ:

textContent เป็นคุณสมบัติของวัตถุธาตุ องค์ประกอบใดๆ ในเอกสารมี รวมถึงองค์ประกอบ div ขนาดวิวพอร์ตของเราด้วย

textContent ส่งคืน โหนด โดยเฉพาะ โหนดข้อความ . ตัวอย่างเช่น ข้อความภายในองค์ประกอบย่อหน้า:<p>Some text...</p> , เป็นโหนดข้อความ

viewportDimensionsของเรา ตัวแปรมี <div> . ว่าง องค์ประกอบ. ตอนนี้ส่งคืนโหนดข้อความที่มีค่าพิกเซลตัวเลขระหว่างแท็กองค์ประกอบ div เปิดและปิด:

width + "px" + " x " + height + "px";

width และ height ทั้งสองมีค่าจำนวนพิกเซล เราเพิ่มเฉพาะ 'px' และ ' x ' (สตริง) เพื่อแยกตัวแปรความกว้างและความสูง และเพื่อให้ทุกคนทราบว่าค่าที่เราส่งคืนนั้นเป็นพิกเซล

เพิ่มนาฬิกาจับเวลาถอยหลังด้วย setTimeout()

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

ในการทำเช่นนั้น เราจำเป็นต้องใช้วิธีการอื่นของ Window Object เรียกว่า setTimeout . เมธอดนี้ออกแบบมาเพื่อรัน function() ครั้งเดียว หลัง จำนวนมิลลิวินาทีที่กำหนด

มันทำงานดังนี้:

setTimeout(functionToExecute, 3000);

1,000 มิลลิวินาที =1 วินาที ดังนั้น 3000 ด้านบนจึงหมายถึง 3 วินาที

เราต้องการเรียกใช้ฟังก์ชันอะไร รายการที่ลบองค์ประกอบ div ของ Viewport Dimensions

เรายังไม่มีฟังก์ชันดังกล่าว มาสร้างและเรียกมันว่า removeViewportDimensions() และภายในบล็อคโค้ด { } มาบอกให้ลบองค์ประกอบ div ของเรา

เพิ่มรหัสต่อไปนี้ ด้านบน ฟังก์ชันเหตุการณ์หน้าต่างของคุณ:

function removeViewportDimensions() {
  viewportDimensions.style.display = "none";
}

display คุณสมบัติระบุวิธีหรือ ถ้า องค์ประกอบจะปรากฏขึ้น โดยตั้งค่าเป็น 'none' เรากำลังซ่อนมันอยู่

ตอนนี้เราต้อง โทร removeViewportDimensions()ใหม่ของเรา ฟังก์ชั่นจาก ภายใน บล็อกรหัสเหตุการณ์หน้าต่างของเรา การใช้ setTimeout() จากเดิม ให้เพิ่มดังนี้:

setTimeout(removeViewportDimensions, 3000);

ตอนนี้ทุกครั้งที่กิจกรรมการปรับขนาดหน้าต่างทำงาน setTimeout ตั้งเวลานับถอยหลัง 3 วินาที กล่าวอีกนัยหนึ่ง:เรา ล่าช้า การเรียก removeViewportDimensions() ภายใน 3 วินาที

เมื่อครบ 3 วินาที removeViewportDimensions() ฟังก์ชันถูกเรียกใช้และรันโค้ด:

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

ตอนนี้คุณสมบัติการแสดงผลขององค์ประกอบ div ของเราถูกตั้งค่าเป็น 'none' และถูกซ่อนไว้

แต่เรามีปัญหา

หากคุณพยายามปรับขนาดหน้าต่างเบราว์เซอร์เป็นครั้งที่สอง หลังจากที่ลบองค์ประกอบ div แล้ว องค์ประกอบ div จะไม่แสดงขึ้นอีก นั่นเป็นเพราะว่า สุดท้าย สิ่งที่เกิดขึ้นในโปรแกรมของเราคือองค์ประกอบ div ของเราได้รับ display ตั้งค่าคุณสมบัติเป็น 'none' .

ไม่มีส่วนใดในโปรแกรมที่เราระบุว่าต้องการให้องค์ประกอบ div ปรากฏให้เห็นอีกครั้ง

แต่แล้วทำไมมันจึงปรากฏให้เห็นในครั้งแรก? เพราะโดยค่าเริ่มต้น <div> องค์ประกอบเป็นสิ่งที่เรียกว่า ระดับบล็อก องค์ประกอบ. ซึ่งหมายความว่า display เริ่มต้น ค่าคุณสมบัติคือ 'block' — ซึ่งเป็น มองเห็นได้ ประเภทการแสดงผล

สิ่งสุดท้ายที่เราทำกับองค์ประกอบ div ของเราคือการตั้งค่าที่แสดงเป็น 'none' . ดังนั้นค่านั้นจะ นิ่ง อยู่ในองค์ประกอบเมื่อเรารันเหตุการณ์การปรับขนาดครั้งที่สอง เว้นแต่คุณจะรีเฟรชหน้าของคุณ

นี้ง่ายต่อการแก้ไข เพิ่มสิ่งต่อไปนี้ภายในบล็อกโค้ดของกิจกรรมปรับขนาดหน้าต่าง (ด้านบน setTimeout !):

viewportDimensions.style.display = "block";

ตอนนี้ display . ขององค์ประกอบ div ของเรา ตั้งค่าคุณสมบัติเป็น 'block' ทุกครั้งที่กิจกรรมการปรับขนาดทำงาน ลองปรับขนาดหน้าต่างของคุณ รอให้มันซ่อนองค์ประกอบ div แล้วปรับขนาดอีกครั้ง

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

เยี่ยมมาก แอปของเราใช้งานได้!

ก็.. เรามีปัญหาอื่น หากคุณลองปรับขนาดหน้าต่างเบราว์เซอร์ของคุณเป็นเวลานาน (มากกว่า 3 วินาที) องค์ประกอบ div จะเริ่มกะพริบ ราวกับว่ามีการเปิดและปิดไฟอยู่ตลอดเวลา

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript แอพของเราไม่คู่ควรเท่าไหร่!

มันเกิดขึ้นเพราะเราไม่ได้รีเซ็ตตัวจับเวลาถอยหลังของเรา ทุกครั้งที่เราปรับขนาดวิวพอร์ต เหตุการณ์ของหน้าต่างจะดำเนินการบล็อกโค้ดฟังก์ชันหลายครั้งเท่าที่จะทำได้ ดังนั้น removeViewportDimensions() . ของเรา ฟังก์ชั่น ยัง โทรหลายครั้ง

สิ่งนี้ทำให้แอปของเรามีการเรียกใช้โค้ดที่ทับซ้อนกันจำนวนมาก องค์ประกอบ div ของเรากำลังถูกทุบด้วย display = 'none' คุณสมบัติ และ display = 'block' ทรัพย์สินไปพร้อม ๆ กัน

สิ่งนี้ทำให้องค์ประกอบ div ของเราแสดงและซ่อนอยู่ตลอดเวลา และทำให้เกิดการกะพริบ นี้ดูด เพื่อแก้ปัญหานี้ เราต้องเรียนรู้วิธีการล้างตัวจับเวลา ทำงานใน JavaScript

รีเซ็ตตัวจับเวลาถอยหลัง

เราสามารถล้างตัวจับเวลาที่มีอยู่โดยใช้ clearTimeout() กระบวนการ. วิธีนี้ออกแบบมาเพื่อล้าง ตัวจับเวลาที่ตั้งไว้ด้วย setTimeout กระบวนการ. พวกเขาเป็นเหมือนวิธีการของพันธมิตร

การล้างตัวจับเวลาที่มีอยู่คือสิ่งที่เราต้องการ เราเพียงต้องการเรียกใช้ setTimeout() จับเวลา ครั้งเดียว ต่อเหตุการณ์การปรับขนาด

คุณอาจคิดว่าเราสามารถเรียกใช้ clearTimeout() บน setTimeout() แบบนี้:

clearTimeout(setTimeout);

แต่นั่นจะไม่ทำงาน ในการใช้วิธี clearTimeout() คุณต้องใช้ ทั่วโลก ตัวแปรเมื่อสร้างวิธีการหมดเวลา

JavaScript มีสองขอบเขต:global &local:

  • A ตัวแปรส่วนกลาง เป็นตัวแปรที่ประกาศนอกฟังก์ชัน (ในขอบเขตสากล)
  • A ตัวแปรท้องถิ่น เป็นตัวแปรที่ประกาศภายในฟังก์ชัน (ขอบเขตท้องถิ่น)

ซึ่งหมายความว่าหากเราต้องการล้าง setTimeout() เราต้องกำหนดให้ตัวแปรโกลบอลก่อน แล้ว แล้ว เราสามารถเรียก clearTimeout() บนตัวแปรนั้นเพื่อรีเซ็ต

มาสร้างตัวแปรส่วนกลางของเราและเรียกมันว่า resizeTimeout :

var resizeTimeout;

วางตัวแปรนี้ไว้ที่ด้านบนสุดของไฟล์ JavaScript

ตอนนี้เป็นเพียงตัวแปรว่าง จะว่างเปล่าจนกว่าจะเริ่มกิจกรรมการปรับขนาดครั้งแรก

หากคุณลองใช้ console.log(resizeTimeout) คุณจะได้รับ ไม่ได้กำหนด กลับมา undefined เป็น เท็จ ประเภทค่านิยมซึ่งเป็นสิ่งสำคัญ แล้วคุณจะเข้าใจว่าทำไม เร็วๆ นี้

ตอนนี้ นำ setTimeout() . ของคุณ จากก่อนหน้านี้และกำหนดให้กับ resizeTimeout ตัวแปร:

resizeTimeout = setTimeout(removeViewportDimensions, 3000);

ตอนนี้ resizeTimeout global ทั่วโลก ตัวแปรจะได้รับข้อมูลเกี่ยวกับ setTimeout . ของเรา ทันทีที่กิจกรรมการปรับขนาดทำงาน

ตอนนี้เราสามารถใช้ setTimeout() ทุกครั้งที่กิจกรรมการปรับขนาดหน้าต่างของเราทำงาน ตั้งค่า clearTimeout() บนโกลบอล resizeTimeout ตัวแปร. ชอบสิ่งนี้:

clearTimeout(resizeTimeout);

วางโค้ดด้านบน resizeTimeout = setTimeout() . ของคุณ ดังนั้นมันจึงมีลักษณะดังนี้:

clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(removeViewportDimensions, 3000);

เอาล่ะ เรียกใช้กิจกรรมการปรับขนาดของคุณเป็นเวลา 5-6 วินาที แล้วคุณจะเห็นว่าการกะพริบหายไป!

สับสน? มาพูดถึงสิ่งที่เกิดขึ้นในโค้ดกันดีกว่า

ล่าม JavaScript (เอ็นจิ้นที่ทำงานในเบราว์เซอร์ของคุณ) จะอ่านโค้ดของคุณและ:

  • รันบรรทัดสำหรับบรรทัด
  • ทีละบรรทัด
  • จากบนลงล่าง

รหัสของเราจะดำเนินการต่อไปจากบนลงล่าง ตราบใดที่เราปรับขนาดหน้าต่างของเราต่อไป การปรับขนาดทุกๆเล็กน้อยจะทำให้เกิดเหตุการณ์ในหน้าต่างใหม่ ดังนั้นล่าม JavaScript จะอ่านและรันโค้ดของเราหลายครั้งต่อวินาที

ทุกครั้งที่ฟังก์ชันของเหตุการณ์หน้าต่างของเรารันบล็อกโค้ด clearTimeout() จะรีเซ็ต ที่มีอยู่ นับถอยหลังในคิวบน resizeTimeout ตัวแปร

ใน สุดท้าย โค้ดที่เรากำหนด setTimeout() .

ดังนั้นสิ่งสุดท้ายที่เกิดขึ้นจะเป็น หนึ่ง นับถอยหลัง 3 วินาทีที่กำหนดให้กับ resizeTimeout . การนับถอยหลังที่เริ่มต้นก่อนหน้านั้นจะถูกล้างโดย clearTimeout() .

สุดยอด! รุ่นที่ 1 เสร็จแล้ว คุณสามารถดูรหัสทั้งหมดได้ที่นี่ มาต่อกันที่เวอร์ชัน 2 กัน โดยเราจะเน้นที่ประสิทธิภาพ


ขนาดวิวพอร์ต [เวอร์ชัน 2]

แอพของเราใช้งานได้ แต่ดีพอไหม ในการตอบคำถามนั้น เราจำเป็นต้องพิจารณาสิ่งสำคัญสองประการ:

  • ประสิทธิภาพที่รับรู้:ประสบการณ์ผู้ใช้ (UX)
  • ประสิทธิภาพของฮาร์ดแวร์ (การใช้งาน &ประสิทธิภาพ)

จากมุมมองของ UX ฉันคิดว่าแอปของเราทำงานได้ดี มันมีเวลาตอบสนองที่รวดเร็วและแสดงผลได้ดีมากแม้ในหน้าจอรุ่นเก่า (ฉันทดสอบกับทหารผ่านศึกอายุสองสามทศวรรษ) เราสามารถโต้แย้งเกี่ยวกับการปรับภาพให้เหมาะสมได้เสมอ (การออกแบบ UI) แต่ด้วยฟังก์ชันการทำงานล้วนๆ แอปของเราจึงทำงานได้ดี

แต่จากประสิทธิภาพของฮาร์ดแวร์ มุมมอง แอพของเราจริงๆ แย่. แอปของเราใช้ทรัพยากร CPU และ GPU เป็นจำนวนมาก เมื่อเทียบกับข้อกำหนดทางเทคนิคเพียงเล็กน้อย

คุณสามารถที่จะคิดออกว่าทำไม? ไม่ใช่สิ่งที่คุณคิดตามสัญชาตญาณเมื่อคุณไม่คุ้นเคยกับ ธรรมชาติตะกละ ของงานวินโดว์

นี่คือปัญหา:

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

ฉันวางฟังก์ชันบน CodePen ที่แสดงภาพปัญหา สังเกตว่ามีการดำเนินการกี่ฟังก์ชันต่อวินาที ดูยอดพุ่งทะลุหลังคาอย่างรวดเร็ว:

ตัวอย่าง CodePen

มันแย่มากและไม่จำเป็นเลย! รหัสของเราใช้ทรัพยากรมากกว่าที่ควรจะเป็น

หากต้องการดูว่า CPU และ GPU ของคุณทำงานมากน้อยเพียงใดในระหว่างกิจกรรมของเบราว์เซอร์ คุณเปิด Task Manager ของ Chrome ได้:

สร้างแอปขนาดเล็ก  รับวิวพอร์ต  ที่มีประสิทธิภาพด้วย JavaScript

ลองปรับขนาดหน้าต่างเบราว์เซอร์ของคุณ ภายในตัวจัดการงาน คุณจะได้รับการใช้งาน CPU &GPU % ที่ส่งคืนถัดจากแท็บเบราว์เซอร์ที่ใช้งานอยู่

การใช้ฮาร์ดแวร์แบบสัมบูรณ์เทียบกับแบบสัมพัทธ์

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

แต่จากญาติ มุมมองการใช้งาน แอพของเราไม่มีประสิทธิภาพอย่างน่ากลัว และนั่นคือปัญหา เราไม่อยากติดเป็นนิสัยละเลยการแสดง เพราะมันจะหลอกหลอนเรา และกลับมากัดเราอีก

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

การทำงานของ Window Event

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

มีการดำเนินการฟังก์ชันกี่ครั้งต่อวินาที

อาจเป็นอะไรก็ได้ตั้งแต่ทุกๆ 4 ถึงทุกๆ 40 มิลลิวินาที ขึ้นอยู่กับหลายปัจจัย ได้แก่:

  • ซีพียูและ GPU (ฮาร์ดแวร์) ของคุณมีประสิทธิภาพเพียงใด
  • คุณใช้เบราว์เซอร์ใดอยู่
  • คุณใช้ซอฟต์แวร์ (รหัส) ซับซ้อนแค่ไหน? มันเกี่ยวข้องกับแอนิเมชั่นไหม
  • ขณะนี้ฮาร์ดแวร์ของคุณกำลังทำอะไรอยู่ มีวิดีโอ YouTube แสดงในแท็บอื่นหรือไม่

ฮาร์ดแวร์ของคุณใช้ทรัพยากรที่มีอยู่ ณ เวลาที่คุณขอให้ทำอะไรบางอย่าง

บางครั้งก็ไม่พอ ฟรี ทรัพยากรเพื่อตอบสนองต่อคำขอของคุณทันทีเพราะมีแอพมากมายทำงานพร้อมกัน

ซึ่งอาจทำให้เวลาตอบสนองช้า หน้าจอขัดข้อง การค้าง และปัญหาอื่นๆ

การใช้ฮาร์ดแวร์เพิ่มขึ้น นี่คือเหตุผลที่เราต้องแก้ไขสถานะปัจจุบันของใบสมัครของเรา เพราะเราใส่ใจในประสิทธิภาพ!

หมายเหตุ:ในเบราว์เซอร์รุ่นใหม่ อัตราการดำเนินการขั้นต่ำคือ 4ms ซึ่งในแง่ของอัตราเฟรมอยู่ที่ประมาณ 250fps เป็นไปตามเอกสาร Web API ของ Mozilla

แต่มีข้อมูลที่ขัดแย้งในหัวข้อนี้ ข้อมูลเพิ่มเติมที่นี่

ฟังก์ชัน "ควร" รันบ่อยแค่ไหน

อัตราการเรียกใช้ฟังก์ชันในอุดมคตินั้นขึ้นอยู่กับบริบท เรากำลังทำงานกับแอนิเมชั่นแบบละเอียดหรือไม่? หรืออะไรง่ายๆ เช่น คืนค่าตัวเลขสองสามค่า (เหมือนที่เราทำกับแอปของเรา)

โดยไม่คำนึงถึงบริบท โดยทั่วไป เราไม่ต้องการให้เหตุการณ์หน้าต่างดำเนินการฟังก์ชันต่างๆ ให้มากที่สุด ที่ไร้สาระ นอกจากนี้ยังไม่ฟรี เราต้องหยุดมัน!

เราควบคุมความถี่ในการดำเนินการได้โดยใช้กระบวนการที่เรียกว่า การควบคุมปริมาณ ที่กำลังจะเกิดขึ้นต่อไป

กิจกรรมเร่งเครื่องช่วยชีวิต!

เราสามารถใช้ฟังก์ชันการควบคุมปริมาณ เพื่อจำกัดจำนวนครั้งที่อีก ฟังก์ชั่นทำงานเมื่อเวลาผ่านไป เช่น:“เรียกใช้ฟังก์ชันนี้เพียงครั้งเดียวต่อ 100 มิลลิวินาที”

มาดูฟังก์ชันตัวควบคุมเหตุการณ์พื้นฐาน ซึ่งทริกเกอร์โดยเหตุการณ์การปรับขนาดหน้าต่าง ตัวควบคุมจะเรียกใช้ฟังก์ชันภายนอกทุกๆ 100 มิลลิวินาที

ฟังก์ชันคันเร่งพื้นฐาน:

var throttled;
var delay = 100;

function functionToExecute() {
  console.log("Executed!");
}

function throttler() {
  if (!throttled) {
    functionToExecute();
    throttled = true;
    setTimeout(function() {
      throttled = false;
    }, delay);
  }
}

window.addEventListener("resize", throttler);

ฉันได้เพิ่มพื้นที่ว่างบางส่วนในการจัดรูปแบบโค้ดเพื่อให้อ่านง่ายขึ้น ลองอ่านโค้ดสองสามครั้งแล้วดูว่าคุณเข้าใจไหม

ใช้ CodePen นี้เพื่อทดสอบ คุณจะเห็นว่าเราเรียก functionToExecute() สูงสุด 10 ครั้งต่อวินาที (10 x 100 =1,000 ms =1 วินาที)

ฉันพูดว่า ถึง เพราะฟังก์ชันจะไม่เริ่มทำงานตามอัตราที่คุณตั้งไว้เสมอ ขึ้นอยู่กับว่าฮาร์ดแวร์ของคุณยุ่งแค่ไหนในขณะที่ฟังก์ชันของคุณกำลังทำงาน คุณใช้แอปที่ต้องเสียภาษีพลังงานอื่นๆ ในคอมพิวเตอร์หรือไม่

เกิดอะไรขึ้นในรหัสเค้น:

อย่ากังวลหากคุณไม่เข้าใจสิ่งนี้ในครั้งแรก ฉันใช้เวลายุ่งกับสิ่งนี้มากจนในที่สุดก็คลิก

ที่ด้านบนสุดของไฟล์ JavaScript เราเริ่มต้นด้วยการประกาศ global . สองรายการ ตัวแปร:

var throttled; // Empty variable. Type: undefined (falsy value)

var delay = 100; // Number

หมายเหตุ:ทั่วโลก ตัวแปร หมายความว่าเราสามารถเข้าถึงตัวแปรเหล่านี้ได้จากทุกที่

เราใช้ throttled เป็นจุดอ้างอิงสำหรับ throttler() . ของเรา ฟังก์ชั่นเพื่อตรวจสอบว่ามีฟังก์ชั่นอยู่ในคิวหรือไม่

ก่อน throttler() . ของเรา ฟังก์ชันถูกทริกเกอร์ในครั้งแรก (โดยเหตุการณ์การปรับขนาด) จะไม่มีฟังก์ชันใดให้ดำเนินการในคิว ดังนั้น throttled เริ่มต้นเป็นตัวแปรว่าง

โดยค่าเริ่มต้น ตัวแปรว่างจะมีประเภทค่าเป็น undefined — ซึ่งเป็น เท็จ ประเภทค่า คุณจะเห็นว่าทำไมสิ่งนี้จึงสำคัญในอีกสักครู่

จากนั้นเราจะกำหนดค่าตัวเลข (100) ให้กับตัวแปรที่เรียกว่า delay . นี่คือค่าที่เราใช้เพื่อควบคุม มิลลิวินาที (มิลลิวินาที) อัตราการดำเนินการของ throttler() . ของเรา ฟังก์ชัน

เรากำหนด 100ms ให้กับ delay ตัวแปร. จากนั้นเราก็ส่งตัวแปรนั้นไปยัง setTimeout() . ของ throttler . ตัวควบคุมของเราจะทำงานทุกๆ 100 มิลลิวินาทีหรือ 10 ครั้งต่อวินาที อย่างมากที่สุด

ฉันพูดว่า มากที่สุด เพราะ 100ms จำกัดอัตราการดำเนินการของเรา จริงๆ แล้วเราอาจได้รับการเรียกใช้ฟังก์ชันน้อยกว่า 10 ครั้งต่อวินาที ขึ้นอยู่กับฮาร์ดแวร์และซอฟต์แวร์ของผู้ใช้ของเรา แต่คอมพิวเตอร์ส่วนใหญ่จะยิงได้ 10 ครั้งต่อวินาที

ภายในฟังก์ชัน throttler เราตั้งค่าเงื่อนไข if คำสั่ง:

function throttler() {
  if (!throttled) {
    // code to execute
  }
}

คำสั่งแบบมีเงื่อนไขตรวจสอบว่าเงื่อนไขที่ระบุมีค่าที่เป็นจริงหรือไม่

! สัญลักษณ์ หมายถึง not . ดังนั้น if (!throttled) แปลเป็น:

“ถ้าการควบคุมปริมาณ ไม่ มีค่าความจริงแล้วเรียกใช้ if รหัส”.

“แต่ถ้า throttled ได้ มีค่าความจริงแล้วหยุดอยู่ตรงนั้น!”

ค่า True &False ใน JavaScript คืออะไร

ใน JavaScript มี ความจริง และ ผิดเพี้ยน ค่านิยม ใช่นั่นคือสิ่งที่พวกเขาเรียกว่า สัจธรรมคือคุณค่าที่เป็นจริง เท็จเป็นค่าที่ถือว่าเป็นเท็จ (เซอร์ไพรส์!)

ใน JavaScript มีค่าเท็จหกค่า:

  • undefined
  • null
  • 0
  • '' (สตริงว่าง)
  • NaN
  • false

ใน JavaScript สิ่งใดก็ตามที่ ไม่ใช่ ค่าใดค่าหนึ่งข้างต้นถือเป็นค่าความจริง ง่ายมากใช่มั้ย

Back to the throttler

With that quick lesson on true vs. false, go ahead and check if our throttled variable is indeed undefined, and a falsy value type.

We can quickly find out with console.log() by using !! to check our variable:

var throttled;
console.log(throttled); // undefined
console.log(!!throttled); // false value

!! is a double not operation. It’s a quick way to check for truthy and falsy values.

Great, now we have confirmed that throttled is undefined, and a falsy value type. This means that the very first time our throttler() runs, if ’s condition is indeed met.

// I start out as a falsy value
var throttled;

// I’m triggered by the window resize event
function throttler() {
  // I will only run my { code } if throttled is falsy (it is!)
  if (!throttled) {
    // code to execute
  }
}

Which means that we can move onto what happens inside if’s code block.

Inside if’s code block

Once inside the if statement code block, we immediately start the callback to functionToExecute .

We also set our global throttled variable to true, so it’s no longer undefined (no longer a false value). This stops any future calls to functionToExecute() .

if (!throttled) {
  // Run callback
  functionToExecute();
  // Set global throttled variable to true
  throttled = true;

  // ...
}

But then right below, we use setTimeout to reset the the throttled value back to a falsy value, after the 100ms delay:

function throttler() {
  if (!throttled) {
    // Run callback
    functionToExecute();
    // Set global throttled variable to true
    throttled = true;

    setTimeout(function() {
      // Set throttled back to false after delay
      throttled = false;
    }, delay);
  }
}

So every time the window resize event is running, it triggers the throttler() , which will call functionToExecute every 100ms!

Throttling is power

It’s easier to understand how powerful throttling is if you look at two opposite extremes. Try the following in the throttler function on CodePen:

  • Swap 100 out with 500 and resize your window. Notice how infrequent the functionToExecute() is called (twice per second).
  • Then try to swap out 500 with 16 and resize the window. Now functionToExecute() is bombarded with calls (up to 60 calls per second).

That’s how throttling works. We use it to limit the number of times a function can be called over time. It’s a very easy way to avoid wasting hardware resources. The hard part is finding the “optimal” execution rate for a given function, as no project is the same. More about that later.

Adding the throttler to our project

To apply the throttler function to our own project, we’ll need to do a bit of refactoring to the old code first. Refactoring is when we improve the structure of a program but don’t change the overall functionality.

In other words:we take something that already works, and makes it easier to use and understand.

We no longer want the window resize event to directly execute the function that returns width and height. Instead, we want our throttler function to regulate when and how often the code executes.

Fortunately, the change is simple. We just need to declare a named function and then put all code from the anonymous function inside that named function.

Let’s call the new function getViewportDimensions() :

function getViewportDimensions() {
  var width = window.innerWidth;
  var height = window.innerHeight;

  viewportDimensions.textContent = width + "px" + " x " + height + "px";
  viewportDimensions.style.display = "block";

  clearTimeout(resizeTimeout);
  resizeTimeout = setTimeout(removeViewportDimensions, 3000);
}

Now let’s take the same throttler function from earlier, and replace the functionToExecute() with getViewportDimensions() :

function throttler() {
  if (!throttled) {
    // Callback: the function we want to throttle
    getViewportDimensions();

    throttled = true;

    setTimeout(function() {
      throttled = false;
    }, delay);
  }
}

And finally we add the window resize event listener:

window.addEventListener("resize", throttler);

That’s it, all the hard work is done! Before we wrap up, let’s check our app’s performance, and see if we need to tweak anything.

Hardware Performance vs. Perceived Performance

I don’t know about you, but when I resize my browser window, the width and height numbers are rendering a bit laggy to my screen. It’s not as smooth as it was before we added the throttler.

But that’s not surprising. Before we added the throttler, our code’s execution rate was absurdly high. Now it’s firing around 10 times per second. That’s why it’s a bit laggy now.

There’s no doubt that our app is very performant, from a hardware usage perspective. Our hardware hardly does any work, so it absolutely loves us right now. Which is good.

But what about the perceived performance, from the User Experience point of view? Do our users love us too?

Our rendering should be smoother. Our function execution rate is currently capped at 10 times per second. Let’s increase it. But how much?

We can make it easier to make that decision if we first take a look at the relationship between time and frame rate.

Time vs. Frame Rate

Frame rate is also known as FPS (Frames Per Second). It refers to the number of individual frames displayed per second.

In general, the higher fps the smoother the rendering will be. Video games often need 60fps or more to render smoothly. Most movies and tv series run smoothly at 24-30fps. The more fast-paced action a video or graphic has, the more FPS it needs to render smooth.

For some types of graphics, you can go as low as 15fps and still get a smooth render. But you should rarely go below 15fps because then the perceived performance will start to look noticeably slower.

To find your desired FPS in ms, you divide your target fps with 1000, e.g. 1000/15(fps) =66ms.

Take a look at this FPS to millisecond table:

var 2fps  = 1000/2; // 500ms
var 10fps = 1000/10; // 100ms
var 15fps = 1000/15; // 66ms
var 24fps = 1000/24; // 42ms
var 30fps = 1000/30; // 33ms
var 60fps = 1000/60; // 16ms

Do you recognize something? Our app is currently executing every 100ms, or in frame rate terms:10 frames per second.

Remember, I just said that anything below 15fps will usually appear slow? That could explain why current app’s rendering feels a bit off. There are not enough frames per second to render it smooth. It doesn’t feel instant.

Let’s do some testing

I’ll guarantee that if we crank our Viewport Dimensions feature up to run at 15fps (66ms), it will look significantly smoother than at the current 10fps (100ms).

And it won’t cost too much extra CPU power.

We only need it to be smooth enough.

Like I said earlier, testing opposite extremes is a good way to find out what not to do. Then the question becomes where between the two extremes does your project fit?

Try testing the following frame rates:5fps (500ms), 15fps (66ms), and 60fps (16ms) on your Viewport Dimensions app and see what happens.

What do you think?

5fps looks horrible. It looks broken. That would never work. Our hardware would love it, but our users will hate it!

15fps looks good to me. I wouldn't say that this frame rate is 100% smooth, but it’s clearly better than 10fps.

It’s also important to remember that if your eyes are trained to look for small details, it’s not comparable to the average user’s experience. Most people will consider 15fps smooth, for this type of graphic.

60fps is super smooth — not surprisingly. But honestly, it’s not worth massively extra hardware usage.

I’m going to settle at 15fps for my app.

Why 15 FPS?

The reason I decided on 15fps / 66ms, is because that frame rate is high enough to be perceived as smooth (for most people).

Most software, computers, and displays (screens) can render at 15fps without problems. Even most small, handheld devices can handle 15fps without issue.

The irony of high frame rates is that while logic tells us that more frames per second =smoother, it can also have the opposite effect.

If you try to force a frame rate or refresh rate that your user’s hardware and software can’t handle, the result is often:lagging.

Refresh rate :is how many times your display can redraw your screen. A refresh rate of 45Hz means that your display can redraw it’s entire screen 45 times in 1 second.

Now if your CPU &GPU can generate a frame rate of 60fps, but your display’s refresh rate is only 45Hz, then you have a mismatch. This often causes unpredictable results.

We developers and designers can easily get out of touch with reality. Our development machines are usually more powerful than the average person’s computer.

This is why we need to do solid research and test our assumptions before we push products on the market.

So 15fps is a happy medium that will also work for people who don’t have powerful machines.

Using 15fps will also save us at least 75% of the energy our hardware used before we added the throttler.

Anything below 15fps will usually start to look slow and laggy. It becomes really obvious once you get to around 10fps, which is why our initial frame rate wasn’t good enough.

Here’s an insightful video on the topic of response times which supports my argument for not going below 15fps (on most things).

Context &the Minimum Effective Dose

How much of a perceived difference is there from 15 to 30fps? If you test it out on the app we just built, you’ll be surprised.

You probably won’t be to notice any significant difference. Not a deal-breaking difference. But the price you pay for hardware usage is double up!

High fps costs a lot of hardware power. So why use more than necessary? There’s always a point of diminishing returns. That’s where the Minimum Effective Dose (MED) comes in. You only need enough, to get the desired effect, no more, no less.

Of course, it always depends on the context. Are we talking about an explosive first-person shooter video game or a simple pixel counter like the one we’ve built? Context is key!

A Finishing Touch

All the hard work is done. For good measure, let’s wrap all our code inside an IIFE ((function () {...})(); ) and initialize use strict mode inside:

(function() {
  "use strict";

  // Put all your code below

})();

IIFE (Immediately-Invoked Function Expression), keeps all your code inside a local scope.

If we don’t wrap our code like this, we risk “polluting the global namespace”. This is a fancy way of saying that we risk causing conflicts with other code, outside of our app.

use strict; is also used to prevent conflicts with 3rd party software that don’t follow strict coding rules.

We’re done!

Here is all the code we’ve written. I've added some comments for clarification and some extra white space for readability:

Finished project code:

(function() {
  "use strict";

  // throttled value before window event starts
  var throttled;

  // Delay function calls to 66ms (frame rate: 15fps)
  var delay = 66;

  // Declare empty variable for later use
  var resizeTimeout;

  // Grab body element
  var body = document.querySelector("body");

  // Create element
  var viewportDimensions = document.createElement("div");

  // Style element
  viewportDimensions.style.position = "fixed";
  viewportDimensions.style.right = 0;
  viewportDimensions.style.top = 0;
  viewportDimensions.style.padding = "16px";
  viewportDimensions.style.zIndex = 3;
  viewportDimensions.style.fontSize = "22px";

  // Add div element inside body element
  body.appendChild(viewportDimensions);

  // window.resize callback function
  function getViewportDimensions() {
    var width = window.innerWidth;
    var height = window.innerHeight;

    viewportDimensions.textContent = width + "px" + " x " + height + "px";
    viewportDimensions.style.display = "block";

    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(removeViewportDimensions, 3000);
  }

  // Called from getViewportDimensions()
  function removeViewportDimensions() {
    viewportDimensions.style.display = "none";
  }

  function throttler() {
    // Only run code if’s block if we’re not throttled
    if (!throttled) {
      // Callback: the function we want to throttle
      getViewportDimensions();
      // We're currently throttled!
      throttled = true;
      // Reset throttled variable back to false after delay
      setTimeout(function() {
        throttled = false;
      }, delay);
    }
  }

  // Listen to resize event, and run throttler()
  window.addEventListener("resize", throttler);

  // End of IFFE wrapper
})();

Here’s a CodePen you can play with.

How to use your app on any project

Include a script tag at the bottom of your HTML document, just above your closing body tag (</body> ) and add the path to your js file, like this:

&lt;script src="/js/get-viewport-dimensions.js"&gt;&lt;/script&gt;

Browser Compatibility

Our app is compatible with all modern browsers, and from Internet Explorer 9 and up.

Advanced throttling

What we learned in this tutorial is a great precursor for some the more powerful performance tools on the market.

If you need to boost performance on more complex projects than the one we built, check out the Lodash JavaScript library. Lodash has a lot of performance features and is very popular among developers. You can also learn a lot from digging through the library’s code.

แหล่งข้อมูล

  • Mozilla’s Resize Event docs
  • Mozilla’s Frame Rate docs
  • Video on Response Times
  • Frame Rate vs Refresh Rate
  • Why 66ms when using setTimeout