2
0

responsive.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <!DOCTYPE html><html lang="fr"><head>
  2. <meta charset="utf-8">
  3. <title>Design réactif et </title>
  4. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  5. <meta name="twitter:card" content="summary_large_image">
  6. <meta name="twitter:site" content="@threejs">
  7. <meta name="twitter:title" content="Three.js – Design réactif et ">
  8. <meta property="og:image" content="https://threejs.org/files/share.png">
  9. <link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
  10. <link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
  11. <link rel="stylesheet" href="/manual/resources/lesson.css">
  12. <link rel="stylesheet" href="/manual/resources/lang.css">
  13. <!-- Import maps polyfill -->
  14. <!-- Remove this when import maps will be widely supported -->
  15. <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
  16. <script type="importmap">
  17. {
  18. "imports": {
  19. "three": "../../build/three.module.js"
  20. }
  21. }
  22. </script>
  23. </head>
  24. <body>
  25. <div class="container">
  26. <div class="lesson-title">
  27. <h1>Design réactif et </h1>
  28. </div>
  29. <div class="lesson">
  30. <div class="lesson-main">
  31. <p>Ceci est le second article dans une série traitant de Three.js.
  32. Le premier traitait <a href="fundamentals.html">des principes de base</a>.
  33. Si vous ne l'avez pas encore lu, vous deviriez peut-être commencer par là.</p>
  34. <p>Cet article explique comment rendre votre application Three.js adaptable
  35. à n'importe quelle situation. Rendre une page web adaptable (<em>responsive</em>)
  36. se réfère généralement à faire en sorte que la page s'affiche de manière
  37. appropriée sur des écrans de taille différente, des ordinateurs de bureau
  38. aux <em>smart-phones</em>, en passant par les tablettes.</p>
  39. <p>Concernant Three.js, il y a d'ailleurs davantage de situations à traiter.
  40. Par exemple, un éditeur 3D avec des contrôles à gauche, droite, en haut ou
  41. en bas est quelque chose que nous voudrions gérer. Un schéma interactif
  42. au milieu d'un document en est un autre exemple.</p>
  43. <p>Le dernier exemple que nous avions utilisé est un canvas sans CSS et
  44. sans taille :</p>
  45. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
  46. </pre>
  47. <p>Ce canvas a, par défaut, une taille de 300x150 pixels.
  48. Dans le navigateur, la manière recommandée de fixer la taille
  49. de quelque chose est d'utiliser CSS.</p>
  50. <p>Paramétrons le canvas pour occuper complètement la page en ajoutant
  51. du CSS :</p>
  52. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;style&gt;
  53. html, body {
  54. margin: 0;
  55. height: 100%;
  56. }
  57. #c {
  58. width: 100%;
  59. height: 100%;
  60. display: block;
  61. }
  62. &lt;/style&gt;
  63. </pre>
  64. <p>En HTML, la balise <em>body</em> a une marge fixée à 5 pixels par défaut donc
  65. la changer à 0 la retire. Modifier la hauteur de <em>html</em> et <em>body</em> à 100%
  66. leur fait occuper toute la fenêtre. Sinon, ils ne sont seulement aussi large
  67. que leur contenu.</p>
  68. <p>Ensuite, nous faisons en sorte que l'élément <code class="notranslate" translate="no">id=c</code> fasse
  69. 100% de la taille de son conteneur qui est, dans ce cas, la balise body.</p>
  70. <p>Finalement, nous passons le mode <code class="notranslate" translate="no">display</code> à <code class="notranslate" translate="no">block</code>.
  71. Le mode d'affichage par défaut d'un canvas est <code class="notranslate" translate="no">inline</code>, ce qui implique
  72. que des espaces peuvent être ajoutés à l'affichage.
  73. En passant le canvas à <code class="notranslate" translate="no">block</code>, ce problème est supprimé.</p>
  74. <p>Voici le résultat :</p>
  75. <p></p><div translate="no" class="threejs_example_container notranslate">
  76. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-no-resize.html"></iframe></div>
  77. <a class="threejs_center" href="/manual/examples/responsive-no-resize.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
  78. </div>
  79. <p></p>
  80. <p>Le canvas, comme nous le voyons, remplit maintenant la page mais il y a deux
  81. problèmes. Tout d'abord, nos cubes sont étirés et ressemblent à des boîtes trop
  82. hautes et trop larges. Ouvrez l'exemple dans sa propre fenêtre et
  83. redimensionnez la, vous verrez comment les cubes s'en trouvent déformés
  84. en hauteur et en largeur.</p>
  85. <p><img src="../resources/images/resize-incorrect-aspect.png" width="407" class="threejs_center nobg"></p>
  86. <p>Le second problème est qu'ils semblent affichés en basse résolution ou
  87. à la fois flous et pixellisés. Si vous étirez beaucoup la fenêtre, vous verrez
  88. pleinement le problème.</p>
  89. <p><img src="../resources/images/resize-low-res.png" class="threejs_center nobg"></p>
  90. <p>Tout d'abord, nous allons résoudre le problème d'étirement.
  91. Pour cela, nous devons calquer l'aspect de la caméra sur celui
  92. de la taille d'affichage du canvas. Nous pouvons le faire
  93. en utilisant les propriétés <code class="notranslate" translate="no">clientWidth</code> et <code class="notranslate" translate="no">clientHeight</code> du canvas.</p>
  94. <p>Nous mettons alors notre boucle de rendu comme cela :</p>
  95. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
  96. time *= 0.001;
  97. + const canvas = renderer.domElement;
  98. + camera.aspect = canvas.clientWidth / canvas.clientHeight;
  99. + camera.updateProjectionMatrix();
  100. ...
  101. </pre>
  102. <p>A présent les cubes ne devraient plus être déformés.</p>
  103. <p></p><div translate="no" class="threejs_example_container notranslate">
  104. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-update-camera.html"></iframe></div>
  105. <a class="threejs_center" href="/manual/examples/responsive-update-camera.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
  106. </div>
  107. <p></p>
  108. <p>Ouvrez l'exemple dans une fenêtre séparée et redimensionnez la.
  109. Vous devriez voir que les cubes ne sont plus étirés, que ce soit
  110. en hauteur ou en largeur.
  111. Ils restent corrects quelque soit l'aspect de la taille de la fenêtre.</p>
  112. <p><img src="../resources/images/resize-correct-aspect.png" width="407" class="threejs_center nobg"></p>
  113. <p>Maintenant résolvons le problème de la pixellisation.</p>
  114. <p>Les éléments de type <em>canvas</em> ont deux tailles. La première
  115. est celle du canvas affiché dans la page. C'est ce que nous paramétrons avec le CSS.
  116. L'autre taille est le nombre de pixels dont est constitué le canvas lui-même.
  117. Ceci n'est pas différent d'une image.
  118. Par exemple, nous pouvons avoir une image de taille 128x64 et, en utilisant le CSS,
  119. nous pouvons l'afficher avec une taille de 400x200.</p>
  120. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;img src="some128x64image.jpg" style="width:400px; height:200px"&gt;
  121. </pre>
  122. <p>La taille interne d'un canvas, sa résolution, est souvent appelée sa taille de tampon
  123. de dessin (<em>drawingbuffer</em>). Dans Three.js, nous pouvons ajuster la taille
  124. du canvas en appelant <code class="notranslate" translate="no">renderer.setSize</code>.
  125. Quelle taille devons nous choisir ? La réponse la plus évidente est "la même taille que
  126. celle du canvas". A nouveau, pour le faire, nous pouvons recourir
  127. aux propriétés <code class="notranslate" translate="no">clientWidth</code> et <code class="notranslate" translate="no">clientHeight</code>.</p>
  128. <p>Ecrivons une fonction qui vérifie si le rendu du canvas a la bonne taille et l'ajuste en conséquence.</p>
  129. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function resizeRendererToDisplaySize(renderer) {
  130. const canvas = renderer.domElement;
  131. const width = canvas.clientWidth;
  132. const height = canvas.clientHeight;
  133. const needResize = canvas.width !== width || canvas.height !== height;
  134. if (needResize) {
  135. renderer.setSize(width, height, false);
  136. }
  137. return needResize;
  138. }
  139. </pre>
  140. <p>Remarquez que nous vérifions si le canvas a réellement besoin d'être redimensionné.
  141. Le redimensionnement est une partie intéressante de la spécification du canvas
  142. et il est mieux de ne pas lui donner à nouveau la même taille s'il est déjà
  143. à la dimension que nous voulons.</p>
  144. <p>Une fois que nous savons si le redimensionnement est nécessaire ou non, nous
  145. appelons <code class="notranslate" translate="no">renderer.setSize</code> et lui passons les nouvelles largeur et hauteur.
  146. Il est important de passer <code class="notranslate" translate="no">false</code> en troisième.
  147. <code class="notranslate" translate="no">render.setSize</code> modifie par défaut la taille du canvas dans le CSS, mais ce n'est
  148. pas ce que nous voulons. Nous souhaitons que le navigateur continue à fonctionner
  149. comme pour les autres éléments, en utilisant le CSS pour déterminer la
  150. taille d'affichage d'un élément. Nous ne voulons pas que les canvas utilisés
  151. par Three.js aient un comportement différent des autres éléments.</p>
  152. <p>Remarquez que notre fonction renvoie <em>true</em> si le canvas a été redimensionné.
  153. Nous pouvons l'utiliser pour vérifier si d'autre choses doivent être mises à jour.
  154. Modifions à présent notre boucle de rendu pour utiliser la nouvelle fonction :</p>
  155. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
  156. time *= 0.001;
  157. + if (resizeRendererToDisplaySize(renderer)) {
  158. + const canvas = renderer.domElement;
  159. + camera.aspect = canvas.clientWidth / canvas.clientHeight;
  160. + camera.updateProjectionMatrix();
  161. + }
  162. ...
  163. </pre>
  164. <p>Puisque l'aspect ne change que si la taille d'affichage du canvas change,
  165. nous ne modifions l'aspect de la caméra que si <code class="notranslate" translate="no">resizeRendererToDisplaySize</code>
  166. retourne <code class="notranslate" translate="no">true</code>.</p>
  167. <p></p><div translate="no" class="threejs_example_container notranslate">
  168. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive.html"></iframe></div>
  169. <a class="threejs_center" href="/manual/examples/responsive.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
  170. </div>
  171. <p></p>
  172. <p>Le rendu devrait à présent avoir une résolution correspondant à
  173. la taille d'affichage du canvas.</p>
  174. <p>Afin de comprendre pourquoi il faut laisser le CSS gérer le redimensionnement,
  175. prenons notre code et mettons le dans un <a href="../examples/threejs-responsive.js">fichier <code class="notranslate" translate="no">.js</code> séparé</a>. Voici donc quelques autres exemples où nous avons laissé le CSS choisir la taille et remarquez que nous n'avons
  176. eu aucun code à modifier pour qu'ils fonctionnent.</p>
  177. <p>Mettons nos cubes au milieu d'un paragraphe de texte.</p>
  178. <p></p><div translate="no" class="threejs_example_container notranslate">
  179. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-paragraph.html&amp;startPane=html"></iframe></div>
  180. <a class="threejs_center" href="/manual/examples/responsive-paragraph.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
  181. </div>
  182. <p></p>
  183. <p>et voici notre même code utilisé dans un éditeur où la zone de contrôle à droite peut être redimensionnée.</p>
  184. <p></p><div translate="no" class="threejs_example_container notranslate">
  185. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-editor.html&amp;startPane=html"></iframe></div>
  186. <a class="threejs_center" href="/manual/examples/responsive-editor.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
  187. </div>
  188. <p></p>
  189. <p>Le point important à remarquer est que le code n'est pas modifié, seulement
  190. le HTML et le CSS.</p>
  191. <h2 id="g-rer-les-affichages-hd-dpi">Gérer les affichages HD-DPI</h2>
  192. <p>HD-DPI est l'acronyme pour <em>high-density dot per inch</em>,
  193. autrement dit, les écrans à haute densité d'affichage.
  194. C'est le cas de la plupart des Macs, des machines sous Windows
  195. ainsi que des smartphones.</p>
  196. <p>La façon dont cela fonctionne dans le navigateur est
  197. qu'il utilise les pixels CSS pour mettre à jour la taille
  198. qui est supposée être la même quelque soit la résolution de
  199. l'affichage. Le navigateur effectue le rendu du texte avec davantage
  200. de détails mais la même taille physique.</p>
  201. <p>Il y a plusieurs façons de gérer les HD-DPI avec Three.js.</p>
  202. <p>La première façon est de ne rien faire de spécial. Cela
  203. est, de manière discutable, le plus commun. Effectuer le
  204. rendu de graphismes 3D réclame beaucoup de puissance de calcul au GPU
  205. (<em>Graphics Processing Units</em>, les processeurs dédiés de carte graphique).
  206. Les GPUs des smartphones ont moins de puissance que ceux des ordinateurs de bureau,
  207. du moins en 2018, et pourtant les téléphones mobiles ont des affichages
  208. haute résolution. Le haut de gamme actuel pour les smartphones a un ratio
  209. HD-DPI de 3x, ce qui signifie que pour chaque pixel d'un affichage non HD-DPI,
  210. ces téléphones ont 9 pixels. Il y a donc 9 fois plus de travail
  211. pour le rendu.</p>
  212. <p>Calculer pour 9 pixels nécessite des ressources. Donc, si
  213. nous laissons le code comme cela, nous calculerons pour 1 pixel
  214. et le navigateur le dessinera avec 3 fois sa taille (3 x 3 = 9 pixels).</p>
  215. <p>Pour toute application Three.js lourde, c'est probablement ce que vous
  216. voulez sinon vous risquez d'avoir un taux de rafraîchissement faible (<em>framerate</em>).</p>
  217. <p>Ceci étant dit, si vous préférez effectuer le rendu à la résolution de l'appareil,
  218. voici quelques façons de le faire en Three.js.</p>
  219. <p>La première est d'indiquer à Three.js le facteur de multiplication de la résolution
  220. en utilisant <code class="notranslate" translate="no">renderer.setPixelRatio</code>. Nous pouvons demander au navigateur ce
  221. facteur entre les pixels CSS et les pixels du périphérique et les passer à Three.js</p>
  222. <pre class="prettyprint showlinemods notranslate notranslate" translate="no"> renderer.setPixelRatio(window.devicePixelRatio);
  223. </pre><p>Après cela, tout appel à <code class="notranslate" translate="no">renderer.setSize</code> va automatiquement
  224. utiliser la taille que vous avez demandé, multiplié par le
  225. ratio que vous avez demandé.
  226. <strong>Ceci est fortement DÉCONSEILLÉ</strong>. Voir ci-dessous.</p>
  227. <p>L'autre façon est de le faire par soi-même quand on redimensionne le canvas.</p>
  228. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> function resizeRendererToDisplaySize(renderer) {
  229. const canvas = renderer.domElement;
  230. const pixelRatio = window.devicePixelRatio;
  231. const width = canvas.clientWidth * pixelRatio | 0;
  232. const height = canvas.clientHeight * pixelRatio | 0;
  233. const needResize = canvas.width !== width || canvas.height !== height;
  234. if (needResize) {
  235. renderer.setSize(width, height, false);
  236. }
  237. return needResize;
  238. }
  239. </pre>
  240. <p>Cette seconde façon est objectivement meilleure. Pourquoi ? Parce que cela signifie
  241. que nous avons ce que nous avons demandé. Il y a plusieurs cas où,
  242. quand on utilise Three.js, nous avons besoin de connaître la taille effective
  243. du tampon d'affichage du canvas. Par exemple, quand on réalise un filtre de
  244. post-processing, ou si nous faisons un <em>shader</em> qui accède à <code class="notranslate" translate="no">gl_FragCoord</code>,
  245. si nous sommes en train de faire une capture d'écran, ou en train de lire les pixels
  246. pour une sélection par GPU, pour dessiner dans un canvas 2D, etc...
  247. Il y a plusieurs cas où, si nous utilisons <code class="notranslate" translate="no">setPixelRatio</code> alors notre
  248. taille effective est différente de la taille que nous avons demandé et nous
  249. aurons alors à deviner quand utiliser la taille demandée ou la taille utilisée
  250. par Three.js.
  251. En le faisant par soi-même, nous savons toujours que la taille utilisée
  252. est celle que nous avons demandé. Il n'y a aucun cas où cela se fait tout
  253. seul autrement.</p>
  254. <p>Voici un exemple utilisant le code vu plus haut.</p>
  255. <p></p><div translate="no" class="threejs_example_container notranslate">
  256. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/responsive-hd-dpi.html"></iframe></div>
  257. <a class="threejs_center" href="/manual/examples/responsive-hd-dpi.html" target="_blank">Cliquer ici pour ouvrir dans une fenêtre séparée</a>
  258. </div>
  259. <p></p>
  260. <p>Il vous est peut-être difficile de voir la différence, mais si vous avez
  261. un affichage HD-DPI et que vous comparez cet exemple aux autres plus
  262. haut, vous devriez remarquer que les arêtes sont plus vives.</p>
  263. <p>Cet article a couvert un sujet très basique mais fondamental.
  264. Dans l'article suivant, nous allons rapidement
  265. <a href="primitives.html">passer en revue les primitives de base proposées par Three.js</a>.</p>
  266. </div>
  267. </div>
  268. </div>
  269. <script src="/manual/resources/prettify.js"></script>
  270. <script src="/manual/resources/lesson.js"></script>
  271. </body></html>