1
 42

Draggable

La classe Draggable ajoute la capacité de déplacer un élément avec la souris ou avec un doigt.

Cliquez dans l'image pour démarrer l'animation. Cliquez de nouveau pour l'arrêter. Placez le pointeur de la souris sur l'image et appuyez sur la barre d'espace. Glissez le clip dans la <div> qui le contient avec la souris ou avec un doigt sur un écran tactile. Déplacez le clip près du bord droit de la <div>. Essayez de la redimensionner.

  1. function Draggable() {
  2.     View.call(this);
  3.  
  4.     this._draggable = false;
  5.     this._dragged = false;
  6. }
  7.  
  8. Draggable.prototype = Object.create(View.prototype);
  9.  
  10. Object.defineProperty(Draggable.prototype, 'constructor', { value: Draggable, enumerable: false, writable: true });
  11.  
  12. Draggable.prototype.isDraggable = function() {
  13.     return this._draggable;
  14. };
  15.  
  16. Draggable.prototype.enableDrag = function() {
  17.     if (this._draggable)
  18.         return this;
  19.  
  20.     if (!this.isManaged())
  21.         return this;
  22.  
  23.     if (this._drag === undefined) {
  24.         let xmax, ymax;
  25.         let x0, y0;
  26.  
  27.         this._drag = (e) => {
  28.             let x1, y1;
  29.  
  30.             switch (e.type) {
  31.                 case 'mousemove':
  32.                     x1 = Math.max(e.clientX, 0);
  33.                     y1 = Math.max(e.clientY, 0);
  34.                     break;
  35.  
  36.                 case 'touchmove':
  37.                     x1 = Math.max(e.changedTouches[0].clientX, 0);
  38.                     y1 = Math.max(e.changedTouches[0].clientY, 0);
  39.                     break;
  40.  
  41.                 default:
  42.                     return;
  43.             }
  44.  
  45.             const dx = x0 - x1;
  46.             const dy = y0 - y1;
  47.  
  48.             if (dx == 0 && dy == 0)
  49.                 return;
  50.  
  51.             x0 = x1;
  52.             y0 = y1;
  53.  
  54.             const w = this._widget;
  55.  
  56.             const x = Math.max(0, Math.min(xmax, w.offsetLeft - dx));
  57.             const y = Math.max(0, Math.min(ymax, w.offsetTop - dy));
  58.  
  59.             w.style.left = x + 'px';
  60.             w.style.top = y + 'px';
  61.  
  62.             this._dragged = true;
  63.         };
  64.  
  65.         this._startdrag = (e) => {
  66.             switch (e.type) {
  67.                 case 'mousedown':
  68.                     x0 = e.clientX;
  69.                     y0 = e.clientY;
  70.  
  71.                     document.addEventListener('mousemove', this._drag);
  72.                     document.addEventListener('mouseup', this._stopdrag);
  73.  
  74.                     e.preventDefault();
  75.                     break;
  76.  
  77.                 case 'touchstart':
  78.                     x0 = e.touches[0].clientX;
  79.                     y0 = e.touches[0].clientY;
  80.  
  81.                     document.addEventListener('touchmove', this._drag);
  82.                     document.addEventListener('touchend', this._stopdrag);
  83.                     break;
  84.  
  85.                 default:
  86.                     return;
  87.             }
  88.  
  89.             this._dragged = false;
  90.         };
  91.  
  92.         this._stopdrag = (e) => {
  93.             switch (e.type) {
  94.                 case 'mouseup':
  95.                     document.removeEventListener('mousemove', this._drag);
  96.                     document.removeEventListener('mouseup', this._stopdrag);
  97.  
  98.                     e.preventDefault();
  99.                     break;
  100.  
  101.                 case 'touchend':
  102.                     document.removeEventListener('touchmove', this._drag);
  103.                     document.removeEventListener('touchend', this._stopdrag);
  104.                     break;
  105.  
  106.                 default:
  107.                     return;
  108.             }
  109.         };
  110.  
  111.         this._fitin = () => {
  112.             const w = this._widget;
  113.             const cw = this._parent;
  114.  
  115.             xmax = cw.offsetWidth - w.width;
  116.             ymax = cw.offsetHeight - w.height;
  117.  
  118.             const x = Math.max(0, Math.min(xmax, w.offsetLeft));
  119.             const y = Math.max(0, Math.min(ymax, w.offsetTop));
  120.  
  121.             if (x != w.offsetLeft || y != w.offsetTop) {
  122.                 w.style.left = x + 'px';
  123.                 w.style.top = y + 'px';
  124.             }
  125.         };
  126.  
  127.         this._dragobserver = new ResizeObserver(this._fitin);
  128.     }
  129.  
  130.     this._parent.style.position = 'relative';
  131.  
  132.     this._dragobserver.observe(this._parent);
  133.  
  134.     this.addEventListener('mousedown', this._startdrag);
  135.     this.addEventListener('touchstart', this._startdrag);
  136.  
  137.     this.setStyle('position', 'absolute');
  138.  
  139.     this.setStyle('touchAction', 'none');
  140.  
  141.     this._draggable = true;
  142.     this._dragged = false;
  143.  
  144.     return this;
  145. };
  146.  
  147. Draggable.prototype.disableDrag = function() {
  148.     if (!this._draggable)
  149.         return this;
  150.  
  151.     this._dragobserver.unobserve(this._parent);
  152.  
  153.     this.removeEventListener('touchstart', this._startdrag);
  154.     this.removeEventListener('mousedown', this._startdrag);
  155.  
  156.     this.setStyle('touchAction', 'auto');
  157.  
  158.     this._draggable = false;
  159.     this._dragged = false;
  160.  
  161.     return this;
  162. };
Test
  1. function NoiseClip() {
  2.     ProgramClip.call(this);
  3.  
  4.     this._interval = NoiseClip.playbackRate;
  5.  
  6.     this._imageData = null;
  7. }
  8.  
  9. NoiseClip.prototype = Object.create(ProgramClip.prototype);
  10.  
  11. Object.defineProperty(NoiseClip.prototype, 'constructor', { value: NoiseClip, enumerable: false, writable: true });
  12.  
  13. NoiseClip.playbackRate = 100;

Une instance de NoiseClip hérite de la classe ProgramClip. La propriété _interval est héritée de la classe Clip. Elle définit la fréquence de l'animation du clip. La propriété _imagedata est un objet ImageData qui contient les données des pixels du canevas de l'image du clip.

  1. Object.assign(NoiseClip.prototype, Draggable.prototype);

Étend la classe NoiseClip avec la classe Draggable.

  1. Object.defineProperty(NoiseClip.prototype, 'playbackRate', {
  2.     get:    function() {
  3.         return this._playbackRate;
  4.     },
  5.     set:    function(r) {
  6.         if (typeof r !== 'number')
  7.             throw new TypeError();
  8.  
  9.         if (r != NoiseClip.playbackRate)
  10.             throw new RangeError();
  11.     }
  12. });

Redéfinit l'accesseur playrate hérité de la classe ProgramClip. playrate empêche tout changement de la fréquence de l'animation d'une instance de NoiseClip.

  1. NoiseClip.prototype.enablePlayer = function() {
  2.     if (this._player)
  3.         return this;
  4.  
  5.     if (this._click === undefined) {
  6.         this._click = (e) => {
  7.             switch (e.type) {
  8.                 case 'click':
  9.                     if (this._dragged)
  10.                         break;
  11.  
  12.                     if (this._paused)
  13.                         this.play();
  14.                     else
  15.                         this.pause();
  16.                     break;
  17.             }
  18.         };
  19.     }
  20.  
  21.     if (this._keydown === undefined) {
  22.         this._keydown = (e) => {
  23.             e.preventDefault();
  24.  
  25.             switch (e.key) {
  26.                 case ' ':
  27.                     if (this._paused)
  28.                         this.play();
  29.                     else
  30.                         this.pause();
  31.                     break;
  32.             }
  33.         };
  34.     }
  35.  
  36.     ProgramClip.prototype.enablePlayer.call(this);
  37.  
  38.     return this;
  39. };

Redéfinit la méthode enablePlayer héritée de la classe Clip.

  1. NoiseClip.prototype.setWidget = function(w) {
  2.     if (w.tagName != 'CANVAS')
  3.         throw new TypeError();
  4.  
  5.     if (w.width == 0 || w.height == 0)
  6.         throw new TypeError();
  7.  
  8.     ProgramClip.prototype.setWidget.call(this, w);
  9.  
  10.     const ctx = w.getContext('2d');
  11.  
  12.     const imgdata = ctx.createImageData(w.width, w.height);
  13.  
  14.     const size = imgdata.width * imgdata.height * 4;
  15.  
  16.     for (let i = 0; i < size; i += 4)
  17.         imgdata.data[i+3] = 255;
  18.  
  19.     this._imageData = imgdata;
  20.  
  21.     window.addEventListener('load', () => this.drawWidget());
  22.  
  23.     return this;
  24. };

Redéfinit la méthode setWidget héritée de la classe Clip.

  1. NoiseClip.prototype.drawWidget = function() {
  2.     const imgdata = this._imageData;
  3.  
  4.     const size = imgdata.width * imgdata.height * 4;
  5.  
  6.     for (let i = 0; i < size; i += 4) {
  7.         imgdata.data[i+0] = Math.random() > 0.5 ? 255 : 0;
  8.         imgdata.data[i+1] = Math.random() > 0.5 ? 255 : 0;
  9.         imgdata.data[i+2] = Math.random() > 0.5 ? 255 : 0;
  10.     }
  11.  
  12.     const ctx = this._widget.getContext('2d');
  13.  
  14.     ctx.putImageData(imgdata, 0, 0);
  15.  
  16.     return this;
  17. };

Redéfinit la méthode drawWidget héritée de la classe ProgramClip.

  1. <?php $size=100; ?>

Définit la taille de l'image du clip, un carré, en pixels.

  1. <?php $id=uniqid('id'); ?>

Définit l'identifiant de la <div> qui encadre le HTML du clip.

  1. #<?php echo $id; ?> {
  2.     min-width: <?php echo $size+20; ?>px;
  3.     min-height: <?php echo $size+20; ?>px;
  4.     max-width: 400px;
  5.     max-height: 200px;
  6.     resize: both;
  7.     overflow: hidden;
  8.     background-color: #fcc;
  9.     background-image: linear-gradient(to right bottom, white 5%, transparent 75%);
  10. }
  11. @media screen and (max-width:410px) {
  12.     #<?php echo $id; ?> {
  13.         max-width: 100%;
  14.     }
  15. }

Définit le CSS de la <div> qui contient le clip.

  1. <div id="<?php echo $id; ?>" class="noprint">
  2. <canvas width="<?php echo $size; ?>" height="<?php echo $size; ?>"></canvas>
  3. </div>

L'image du clip est dessinée dans un canevas.

  1. <?php head('javascript', '/objectivejs/Objective.js'); ?>
  2. <?php head('javascript', '/objectivejs/Responder.js'); ?>
  3. <?php head('javascript', '/objectivejs/View.js'); ?>
  4. <?php head('javascript', '/objectivejs/Clip.js'); ?>
  5. <?php head('javascript', '/objectivejs/ProgramClip.js'); ?>
  6. <?php head('javascript', '/objectivejs/Draggable.js'); ?>
  7. <?php head('javascript', '/objectivejs/tests/NoiseClip.js'); ?>

Inclut le code de toutes les classes nécessaires. RAPPEL : La fonction head de la librairie iZend ajoute une balise telle que <script src="/objectivejs/Objective.js"></script> à la section <head> du document HTML. Adaptez le code à votre environnement de développement.

  1. const noise = new NoiseClip();
  2.  
  3. noise.setManagedWidget(document.querySelector('#<?php echo $id; ?> canvas'));

Crée une instance de NoiseClip et l'associe au canevas. L'image initiale est automatiquement dessinée dès que la fenêtre est complètement chargée.

  1. noise.enablePlayer();

Active le contrôle de l'animation.

  1. noise.enableDrag();

Active le contrôle du déplacement du clip.

VOIR AUSSI

ProgramClip, Clip

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].