ניסוי: כמה איטי האינטרנט שלנו?

משחקים עם קוד: כיצד לבנות ויזואליזציה להמחשת איטיות מהירות האינטרנט בישראל, לעומת שאר העולם.

תמונה: flickr, cc-by, Takashi(aes256)

תמונה: flickr, cc-by, Takashi(aes256)

מטרת הניסוי

בניית ויזואליזציה להמחשת איטיות מהירות האינטרנט בישראל, לעומת שאר העולם.

הציוד הדרוש

jQuery, Backbone.js, Underscore.js

רקע

מהירות החיבור לאינטרנט בישראל, איטית מאוד יחסית לעולם.
הניסוי הבא ימחיש את המהירות בארץ, ע”י מירוץ מכוניות.
כל מכונית שמשתתפת במירוץ, מייצגת מדינה אחרת.
ככל שמהירות החיבור גבוהה יותר באותה מדינה – כך המכונית תיסע מהר יותר ותסיים את המסלול מוקדם יותר.
בסוף המירוץ, תוצג רשימת המדינות המלאה, מיקומן ביחס לאחרות ומהירות החיבור בהן.
הויזואליזציה עושה שימוש בנתונים מתוך מאגר ה-STS (מאגר הנתונים של איגוד האינטרנט הישראלי).

מהלך הניסוי

index.html

[html]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link href="css/style.css" rel="stylesheet" type="text/css">
<title>מרוץ מהירויות החיבור</title>
</head>
<body>

<script type="text/javascript" src="js/script.js"></script>
</body>
</html>
[/html]

נוסיף 3 ספריות ג’אווה סקריפט:

  • jQuery לבחירת אלמנטים ושינוי המאפיינים שלהם (כיתוב, צבע…).
  • Backbone.js לארגון הקוד במבנה MVC.
  • Underscore.js לעבודה עם תבניות.

[html]
<body>

<script type="text/javascript" src="js/libs/jquery.min.js"></script>
<script type="text/javascript" src="js/libs/underscore-min.js"></script>
<script type="text/javascript" src="js/libs/backbone-min.js"></script>
<script type="text/javascript" src="js/script.js"></script>
</body>
[/html]

נגדיר תבניות

לשימוש בתבניות יש 2 מטרות מרכזיות:
1. לחסוך בכמות הקוד (נחסכת הוספה ידנית של כל פריט בנפרד ב-HTML ובג’אווה סקריפט).
2. להגביר את יעילות הקוד (הפריטים שבתבנית הם כבר לא טקסט חסר משמעות כמו בעידן הפרה-תבניתי, אלא אובייקט יציב ובעל משמעות).

  1. תבנית דגלים
    [javascript]
    <script type="text/template" id="flag_template">
    <h4>מקום <%= place %></h4>
    <h5><%= name %></h5>
    <img id="flag_<%= id %>" src="http://www.geonames.org/flags/x/<%= id %>.gif" width="50" height="30" alt="<%= name %>" />
    <h4><%= speed %> Kbit/s</h4>
    </script>
    [/javascript]
  2. תבנית מכוניות
    [javascript]
    <script type="text/template" id="car_template">
    <img id="car_flag_<%= id %>" class="flag" src="http://www.geonames.org/flags/x/<%= id %>.gif" width="20" height="15" alt="<%= name %>" />
    <embed id="car_<%= id %>" src="images/car/<%= colorId%>.svg" width="200"></embed>
    </script>
    [/javascript]


  3. תבנית מד-מהירות
    [javascript]
    <script type="text/template" id="meter_template">
    <%= name %><br />
    <progress id="car_progress_<%= id %>" class="progress" max="<%= maximumMeterValue %>"></progress><br />
    <span id="car_completed_<%= id %>">0</span> / 10000 Kbits</span>
    </script>
    [/javascript]


Data/flag.json.jpeg

זהו קובץ ששמורים בו נתוני כל המדינות שהשתתפו במחקר המהירויות.
לכל מדינה יש 3 מאפיינים:
1. id – סימול מדינה.
הסימול משמש לקריאה מהאתר GeoNames שמאחסן את תמונות דגלי כל מדינות העולם בפורמט הבא:
http://www.geonames.org/flags/x/[Country ID].gif
2. name – השם כפי שיופיע בויזואליזציה.
3. speed – המהירות.
הקובץ בנוי בפורמט JSON.
הוא שמור בסיומת JPEG כדי שכרום יוכל לקרוא אותו.

[javascript]
[
{
"id": "jp",
"name": "Japan",
"speed": 149.616
},
{
"id": "se",
"name": "Sweden",
"speed": 101.807
},

]
[/javascript]

js/script.js

השימוש ב-Backbone.js מכתיב את מבנה הסקריפט:

  1. מודלים (Model):
    • מכונית:
      [javascript]
      var CarModel = Backbone.Model.extend({});
      [/javascript]

      topPosition – קוארדינטת המיקום העליון.
      colorId – מספר מ-1 עד 5 המייצג קובץ SVG של מכוניות בצבעים שונים.

    • דגל:
      [javascript]
      var FlagModel = Backbone.Model.extend({});
      [/javascript]

      id – מזהה ייחודי עבור GeoNames.
      name – השם שמופיע בסמוך לדגל.
      speed – מהירות הורדה ממוצעת.
      place – מיקום בדו”ח (מקום ראשון מסמל את המהירות הגבוהה מכולן).
      selected – האם המשתמש בחר את הדגל. לחיצה על התצוגה המקושרת למודל, משנה את הערך הבוליאני של התכונה הזאת.

  2. אוספים (Collection):
    • מכוניות:
      [javascript]
      var CarCollection = Backbone.Collection.extend({
      model : CarModel
      });
      [/javascript]

      נוצר דינאמית ומוכנסים אליו מודלים של מכונית, לפי סדר בחירת המדינות.
      לדוגמה: אם בחרנו את המדינות: ישראל (ברירת המחדל), צ’ילה, סלובקיה, יפן וצ’כיה, המכוניות תופענה בסדר הזה מלמעלה-למטה על הכביש.

    • דגלים:
      [javascript]
      var FlagCollection = Backbone.Collection.extend({
      model : FlagModel,
      url : "data/flag.json.jpeg"
      });
      [/javascript]

      נקראים מה-JSON:
      ע”י הגדרת המודל כ-FlagModel, הפריטים שב-JSON הופכים להיות גם מאפיינים של המודל.
      לדוגמה: קריאה של flagModel.speed תציג את הנתון מה-JSON.

  3. תצוגות (View):
    • מכוניות
      נסרוק את אוסף המכוניות:
      [javascript]
      var CarsView = Backbone.View.extend({
      el : ‘#cars’,
      initialize : function() {
      var carsModels = appRouter.carCollection.models;
      this.createCars(carsModels);
      },
      createCars : function(carsModels) {
      var _this = this;
      var colorId = 1;
      var topPosition = 140;
      _.each(carsModels, function(carModel) {

      });
      }
      });
      [/javascript]

      נקבע את מאפייני מודל המכונית:
      topPosition – המיקום העליון (המכוניות מסודרות מלמעלה-למטה בהפרש של 95 פיקסלים).
      colorId – מספר מזהה של צבע, שעולה כל מכונית באחד.

      [javascript]
      carModel.set({
      ‘topPosition’ : topPosition,
      ‘colorId’ : colorId++
      });
      topPosition += 95;
      [/javascript]

      ניצור תצוגת מכונית:

      [javascript]
      var carView = new CarView({
      model : carModel
      });
      [/javascript]

      נציג אותה על המסך:

      [javascript]
      $(_this.el).append(carView.createCar().el);
      [/javascript]

    • מכונית
      נצמיד את התצוגה ל-Class ולתבנית:
      [javascript]
      var CarView = Backbone.View.extend({
      className : "car",
      template : _.template($(‘#car_template’).html()),

      });
      [/javascript]

      הפונקציה הבאה נקראת מתוך תצוגת המכוניות ומקבלת מודל:

      [javascript]
      createCar : function() {
      var carModel = this.model;
      var element = this.getElement(carModel);
      this.setPosition(carModel);
      this.countDown();
      return element;
      },
      getElement : function(carModel) {
      var carElement = this.template(carModel.toJSON());
      $(this.el).append(carElement);
      return this;
      }
      [/javascript]

      קביעת מיקום המכונית:

      [javascript]
      setPosition : function(carModel) {
      $(this.el).css(‘top’, carModel.get(‘topPosition’) + ‘px’);
      }
      [/javascript]

      ספירה לאחור עד לתחילת המירוץ:

      [javascript]
      countDown : function(number) {
      var _this = this;
      this.countStep("3", 500);
      this.countStep("2", 1000);
      this.countStep("1", 2000);
      this.countStep("GO", 3000);
      setTimeout(function() {
      $("#countdown").fadeOut();
      appRouter.isCarDriving = true;
      _this.driveCar();
      }, 4000);
      },
      countStep : function(text, seconds) {
      setTimeout(function() {
      $("#countdown").text(text).fadeIn();
      }, seconds);
      }
      [/javascript]

      הזזת המכונית, ע”י אינטרבל שמשנה את מיקומה בהתאם למהירותה:

      [javascript]
      driveCar : function() {
      if(appRouter.isCarDriving === true) {
      var _this = this;
      _this.interval = setInterval(function() {
      var carId = _this.model.get("id");
      var carSpeed = _this.model.get("speed");
      var currentCarLeft = $(_this.el).position().left;
      _this.animateCar(_this.el, carSpeed, currentCarLeft);
      _this.animateRoad(carId, carSpeed, currentCarLeft);
      _this.increaseProgressValue(carId, carSpeed);
      if(_this.isAllMetersMaximized() === true) {
      _this.endRace();
      }
      }, 1);
      }
      }
      [/javascript]

      אנימציית הזזת המכונית:

      [javascript]
      animateCar : function(carElement, carSpeed, currentCarLeft) {
      var newCarLeft = currentCarLeft + carSpeed;
      $(carElement).css(‘left’, newCarLeft + ‘px’);
      }
      [/javascript]

      אנימציית הזזת האובייקטים ברקע, תוך הצמדתם לשול הימני של המסך:

      [javascript]
      animateRoad : function(carId, carSpeed, currentCarLeft) {
      if(carId === appRouter.focusedCarId) {
      currentRoadLeft = $("#road").position().left;
      newRoadLeft = currentRoadLeft – carSpeed;
      var endRoadLeft = $("#road").width() – $(window).width();
      if(newRoadLeft > -endRoadLeft) {
      $("#road").css(‘left’, newRoadLeft + ‘px’);
      }
      }
      }
      [/javascript]

      אנימציית מילוי מד-המהירות, גם-כן לפי קצב המכונית.
      תוך-כדי בדיקה שהוא לא עובר את המקסימום.

      [javascript]
      increaseProgressValue : function(carId, carSpeed) {
      var maximumMeterValue = $(‘#car_progress_’ + carId).attr(‘max’);
      var currentProgressValue = $(‘#car_progress_’ + carId).attr(‘value’);
      if(currentProgressValue < maximumMeterValue) {
      $(‘#car_progress_’ + carId).attr(‘value’, currentProgressValue + carSpeed);
      var roundedCurrentProgressValue = Math.round(currentProgressValue + carSpeed);
      $(‘#car_completed_’ + carId).text(roundedCurrentProgressValue);
      } else {
      $(‘#car_completed_’ + carId).text(maximumMeterValue);
      }
      },
      isAllMetersMaximized : function() {
      var allMetersMaximized = true;
      $(‘.progress’).each(function(index) {
      var currentProgressValue = $(this).attr(‘value’);
      var currentProgressMaximum = $(this).attr(‘max’);
      if (currentProgressValue < currentProgressMaximum){
      allMetersMaximized = false;
      }
      });
      return allMetersMaximized;
      }
      [/javascript]

      הצגת התוצאות הסופיות וכיבוי האינטרבל.

      [javascript]
      endRace : function() {
      $(‘#road’).css(‘opacity’, ‘0.1’);
      $(‘#meters’).css(‘opacity’, ‘0.1’);
      $(‘#countries’).css(‘background’, ‘rgba(0,0,0,1)’).fadeIn();
      $(‘#countries h4’).show();
      $(‘#restart_race’).show();
      $(‘#subtitle’).show();
      $(‘#meters’).hide();
      clearInterval(this.interval);
      }
      [/javascript]

    • דגלים
      מעבר על מודלי הדגלים והצגתם במסך:
      [javascript]
      var FlagsView = Backbone.View.extend({
      el : ‘#flags’,
      initialize : function() {
      var flagsModels = this.collection.models;
      this.createFlags(flagsModels);
      },
      createFlags : function(flagsModels) {
      var speedPlace = 0;
      var _this = this;
      _.each(flagsModels, function(flagModel) {
      flagModel.set({
      ‘place’ : ++speedPlace
      });
      var flagView = new FlagView({
      model : flagModel
      });
      $(_this.el).append(flagView.createFlag().el);
      });
      }
      });
      [/javascript]
    • דגל
      נצמיד את הדגל לתבנית המתאימה:
      [javascript]
      var FlagView = Backbone.View.extend({
      tagName : "li",
      className : "flag",
      template : _.template($(‘#flag_template’).html()),

      });
      [/javascript]

      נטפל באירוע “לחיצה על דגל”:
      לחיצה מסמנת או מבטלת סימון לסירוגין.
      לא-נאפשר לבחור את ישראל (חייבת להשתתף ומסומנת כברירת-המחדל)
      נעדכן את ספירת המדינות שנבחרו.
      אם 5 מדינות נבחרו, נתחיל את המירוץ.

      [javascript]
      events : {
      "click" : "toggleFlag" //select/unselect flag for racing
      },
      toggleFlag : function(init) {
      $(this.el).toggleClass(‘selected_flag’);
      if(this.model.get(‘id’) !== ‘il’ || init === true) {
      if(this.model.get(‘selected’) === true) {
      this.selectFlag();
      } else {
      this.unselectFlag();
      }
      $("#selected_count").text(appRouter.carCollection.length);
      if(appRouter.carCollection.length === 5) {
      var carsView = new CarsView({
      collection : this.carCollection
      });
      var metersView = new MetersView({
      collection : this.carCollection
      });
      $(‘#subtitle’).hide();
      $(‘#road’).fadeIn();
      }
      }
      },
      selectFlag : function() {
      this.model.set({
      ‘selected’ : false
      });
      appRouter.carCollection.remove(this.model);
      },
      unselectFlag : function() {
      this.model.set({
      ‘selected’ : true
      });
      appRouter.carCollection.add(this.model);
      }
      [/javascript]

      ניצור דגל ונוסיף את ישראל כברירת-המחדל:

      [javascript]
      createFlag : function() {
      var flagModel = this.model;
      var flagElement = this.template(flagModel.toJSON());
      $(this.el).append(flagElement);
      this.addIsraelToCollection();
      return this;
      },
      addIsraelToCollection : function() {
      if(this.model.get(‘id’) === ‘il’) {
      this.toggleFlag(true);
      }
      appRouter.focusedCarId = "il";
      }
      [/javascript]

    • מדי-מהירות
      נציג את מדי-המהירות, כאשר הם מסודרים מלמעלה-למטה.
      ניתן למודל מד-המהירות את התכונות של המכונית, כדי שהמיקום העליון שלהם יהיה זהה.
      [javascript]
      var MetersView = Backbone.View.extend({
      el : ‘#meters’,
      initialize : function() {
      var carsModels = appRouter.carCollection.models;
      this.createMeters(carsModels);
      },
      createMeters : function(carsModels) {
      var _this = this;
      var topPosition = 240;
      _.each(carsModels, function(meterModel) {
      var maximumMeterValue = $(‘#road’).width();
      meterModel.set({
      ‘topPosition’ : topPosition,
      ‘maximumMeterValue’ : maximumMeterValue
      });
      topPosition += 95;
      var meterView = new MeterView({
      model : meterModel
      });
      $(_this.el).append(meterView.createMeter().el);
      });
      }
      });
      [/javascript]
    • מד-מהירות
      ניצור את מד-המהירות ונשים אותו במיקום העליון המתאים לו.
      [javascript]
      var MeterView = Backbone.View.extend({
      className : "meter",
      template : _.template($(‘#meter_template’).html()),
      createMeter : function() {
      var meterModel = this.model;
      var element = this.getElement(meterModel);
      this.setPosition(meterModel);
      return element;
      },
      getElement : function(carModel) {
      var meterElement = this.template(carModel.toJSON());
      $(this.el).append(meterElement);
      return this;
      },
      setPosition : function(carModel) {
      $(this.el).css(‘top’, carModel.get(‘topPosition’) + ‘px’);
      }
      });
      [/javascript]
  4. נתב (Router):

    נאתחל את מרכיבי המירוץ:

    [javascript]
    var AppRouter = Backbone.Router.extend({
    routes : {
    "*actions" : "load"
    },
    load : function() {
    this.preloadImages(["images/car/1.svg", "images/car/2.svg", "images/car/3.svg", "images/car/4.svg", "images/car/5.svg", "images/flag.svg", "images/grass.jpg", "images/lane.gif", "images/margin_bg.png", "images/start.gif"]);
    this.setRestartButton();
    this.setIsCarDriving();
    this.loadFlags();
    this.loadCars();
    }
    });
    [/javascript]

    נאתחל את קבצי התמונות לפני שהדף עולה:

    [javascript]
    preloadImages : function(arrayImages) {
    $(arrayImages).each(function() {
    (new Image()).src = this;
    });
    }
    [/javascript]

    נצמיד אירוע לחיצה ללחצן “התחל מחדש”:

    [javascript]
    setRestartButton : function() {
    $(‘#restart_race’).click(function() {
    window.location = window.location
    })
    }
    [/javascript]

    נאתחל את מצב המכונית:

    [javascript]
    setIsCarDriving : function() {
    this.isCarDriving = false;
    }
    [/javascript]

    נטען את הדגלים מה-JSON ונעבירם לתבנית הדגלים:

    [javascript]
    loadFlags : function() {
    var flagCollection = new FlagCollection();
    flagCollection.fetch({
    success : function(data) {
    var flagsView = new FlagsView({
    collection : data
    })
    return data;
    },
    error : function(response) {
    console.log("Error occured while accessing JSON Data. Response: ", response);
    }
    });
    }
    [/javascript]

    נטען את אוסף המכוניות:

    [javascript]
    loadCars : function() {
    this.carCollection = new CarCollection();
    }
    [/javascript]

תוצאות הניסוי

בדקו היכן ממוקמת ישראל ביחס לשאר העולם, במבחן מהירות האינטרנט (לצפייה בכרום או פיירפוקס)

הפוסט פורסם לראשונה בבלוג “קוד נקי“.

Avatar

שי מסיסטרנו

מפתח ווב מאז 2007, בעברו מרצה במכללת Ness מ-2009 עד 2012.

הגב

2 תגובות על "ניסוי: כמה איטי האינטרנט שלנו?"

avatar
Photo and Image Files
 
 
 
Audio and Video Files
 
 
 
Other File Types
 
 
 

* היי, אנחנו אוהבים תגובות!
תיקונים, תגובות קוטלות וכמובן תגובות מפרגנות - בכיף.
חופש הביטוי הוא ערך עליון, אבל לא נוכל להשלים עם תגובות שכוללות הסתה, הוצאת דיבה, תגובות שכוללות מידע המפר את תנאי השימוש של Geektime, תגובות שחורגות מהטעם הטוב ותגובות שהן בניגוד לדין. תגובות כאלו יימחקו מייד.

סידור לפי:   חדש | ישן | הכי מדורגים
יניב
Guest

טיפן – מהירות מטורפת!!!!!
קוראיה שניה אחרי יפן במהירות..

מדהים

ואנחנו? אנחנו בכל נמצאים מעל מדינות כמו מקסיקו…

News
Guest

המחשה יפה

wpDiscuz

תגיות לכתבה: