tips.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <!DOCTYPE html><html lang="ru"><head>
  2. <meta charset="utf-8">
  3. <title>Советы</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 – Советы">
  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="../resources/lesson.css">
  12. <link rel="stylesheet" href="../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>Советы</h1>
  28. </div>
  29. <div class="lesson">
  30. <div class="lesson-main">
  31. <p>Эта статья представляет собой набор небольших проблем, с которыми вы можете столкнуться при использовании three.js, которые кажутся слишком маленькими, чтобы иметь собственную статью. </p>
  32. <hr>
  33. <p><a id="screenshot" data-toc="Делаем скриншот холста"></a></p>
  34. <h1 id="-">Делаем скриншот холста</h1>
  35. <p>В браузере фактически есть 2 функции, которые сделают скриншот.
  36. Старая
  37. <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL"><code class="notranslate" translate="no">canvas.toDataURL</code></a>
  38. и новая улучшенная
  39. <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob"><code class="notranslate" translate="no">canvas.toBlob</code></a></p>
  40. <p>Таким образом, вы думаете, что было бы легко сделать снимок экрана, просто добавив такой код, как </p>
  41. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
  42. +&lt;button id="screenshot" type="button"&gt;Save...&lt;/button&gt;
  43. </pre>
  44. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const elem = document.querySelector('#screenshot');
  45. elem.addEventListener('click', () =&gt; {
  46. canvas.toBlob((blob) =&gt; {
  47. saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
  48. });
  49. });
  50. const saveBlob = (function() {
  51. const a = document.createElement('a');
  52. document.body.appendChild(a);
  53. a.style.display = 'none';
  54. return function saveData(blob, fileName) {
  55. const url = window.URL.createObjectURL(blob);
  56. a.href = url;
  57. a.download = fileName;
  58. a.click();
  59. };
  60. }());
  61. </pre>
  62. <p>Вот пример из <a href="responsive.html">статьи об отзывчивости</a>
  63. с добавленным выше кодом и некоторым CSS для размещения кнопки </p>
  64. <p></p><div translate="no" class="threejs_example_container notranslate">
  65. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-screenshot-bad.html"></iframe></div>
  66. <a class="threejs_center" href="/manual/examples/tips-screenshot-bad.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  67. </div>
  68. <p></p>
  69. <p>Когда я попробовал это, я получил этот скриншот </p>
  70. <div class="threejs_center"><img src="../resources/images/screencapture-413x313.png"></div>
  71. <p>Да, это просто черное изображение. </p>
  72. <p>Возможно, это сработало для вас в зависимости от вашего браузера / ОС, но в целом это вряд ли сработает. </p>
  73. <p>Проблема заключается в том, что по соображениям производительности и совместимости
  74. браузер по умолчанию очищает буфер рисования WebGL-холста после его отрисовки. </p>
  75. <p>Решение состоит в том, чтобы вызвать ваш код рендеринга непосредственно перед захватом. </p>
  76. <p>В нашем коде нам нужно настроить несколько вещей. Сначала давайте выделим код рендеринга. </p>
  77. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const state = {
  78. + time: 0,
  79. +};
  80. -function render(time) {
  81. - time *= 0.001;
  82. +function render() {
  83. if (resizeRendererToDisplaySize(renderer)) {
  84. const canvas = renderer.domElement;
  85. camera.aspect = canvas.clientWidth / canvas.clientHeight;
  86. camera.updateProjectionMatrix();
  87. }
  88. cubes.forEach((cube, ndx) =&gt; {
  89. const speed = 1 + ndx * .1;
  90. - const rot = time * speed;
  91. + const rot = state.time * speed;
  92. cube.rotation.x = rot;
  93. cube.rotation.y = rot;
  94. });
  95. renderer.render(scene, camera);
  96. - requestAnimationFrame(render);
  97. }
  98. +function animate(time) {
  99. + state.time = time * 0.001;
  100. +
  101. + render();
  102. +
  103. + requestAnimationFrame(animate);
  104. +}
  105. +requestAnimationFrame(animate);
  106. </pre>
  107. <p>Теперь этот <code class="notranslate" translate="no">render</code> касается только фактического рендеринга, мы можем вызвать его непосредственно перед захватом холста. </p>
  108. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const elem = document.querySelector('#screenshot');
  109. elem.addEventListener('click', () =&gt; {
  110. + render();
  111. canvas.toBlob((blob) =&gt; {
  112. saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
  113. });
  114. });
  115. </pre>
  116. <p>И теперь это должно работать. </p>
  117. <p></p><div translate="no" class="threejs_example_container notranslate">
  118. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-screenshot-good.html"></iframe></div>
  119. <a class="threejs_center" href="/manual/examples/tips-screenshot-good.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  120. </div>
  121. <p></p>
  122. <p>Для другого решения см. Следующий пункт. </p>
  123. <hr>
  124. <p><a id="preservedrawingbuffer" data-toc="Предотвращение очистки холста "></a></p>
  125. <h1 id="-">Предотвращение очистки холста</h1>
  126. <p>Допустим, вы хотели, чтобы пользователь рисовал анимированный объект.
  127. Вам нужно передать <code class="notranslate" translate="no">preserveDrawingBuffer: true</code> при создании <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a>.
  128. Это мешает браузеру очистить холст. Вы также должны сказать three.js не очищать холст. </p>
  129. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
  130. -const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  131. +const renderer = new THREE.WebGLRenderer({
  132. + canvas,
  133. + preserveDrawingBuffer: true,
  134. + alpha: true,
  135. +});
  136. +renderer.autoClearColor = false;
  137. </pre>
  138. <p></p><div translate="no" class="threejs_example_container notranslate">
  139. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-preservedrawingbuffer.html"></iframe></div>
  140. <a class="threejs_center" href="/manual/examples/tips-preservedrawingbuffer.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  141. </div>
  142. <p></p>
  143. <p>Обратите внимание, что если вы серьезно относитесь к созданию программы для рисования,
  144. это не будет решением, так как браузер будет очищать холст каждый раз, когда мы меняем его разрешение.
  145. Мы меняем разрешение в зависимости от размера дисплея. Размер дисплея изменяется при изменении размера окна.
  146. Это включает в себя, когда пользователь загружает файл, даже в другой вкладке, и браузер добавляет строку состояния.
  147. Это также включает в себя, когда пользователь поворачивает свой телефон и браузер переключается с портретного на альбомный. </p>
  148. <p>Если вы действительно хотите создать программу для рисования, вы должны .
  149. <a href="rendertargets.html">визуализировать текстуру, используя цель визуализации</a>.</p>
  150. <hr>
  151. <p><a id="tabindex" data-toc="Ввод с клавиатуры"></a></p>
  152. <h1 id="-">Ввод с клавиатуры</h1>
  153. <p>В этих уроках мы часто прикрепляли слушателей событий <code class="notranslate" translate="no">canvas</code>.
  154. Хотя многие события работают, по умолчанию не работают события клавиатуры. </p>
  155. <p>Чтобы получить события клавиатуры, установите для холста <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex"><code class="notranslate" translate="no">tabindex</code></a>
  156. значение 0 или более. Например.</p>
  157. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas tabindex="0"&gt;&lt;/canvas&gt;
  158. </pre>
  159. <p>Это в конечном итоге вызывает новую проблему, хотя. Все, что имеет установленный <code class="notranslate" translate="no">tabindex</code> будет выделено, когда оно будет в фокусе.
  160. Чтобы исправить это, установите фокус CSS <code class="notranslate" translate="no">outline:none</code>. </p>
  161. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">canvas:focus {
  162. outline:none;
  163. }
  164. </pre>
  165. <p>Для демонстрации здесь представлены 3 холста </p>
  166. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c1"&gt;&lt;/canvas&gt;
  167. &lt;canvas id="c2" tabindex="0"&gt;&lt;/canvas&gt;
  168. &lt;canvas id="c3" tabindex="1"&gt;&lt;/canvas&gt;
  169. </pre>
  170. <p>и немного CSS только для последнего холста </p>
  171. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c3:focus {
  172. outline: none;
  173. }
  174. </pre>
  175. <p>Давайте прикрепим к ним всех слушателей событий </p>
  176. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">document.querySelectorAll('canvas').forEach((canvas) =&gt; {
  177. const ctx = canvas.getContext('2d');
  178. function draw(str) {
  179. ctx.clearRect(0, 0, canvas.width, canvas.height);
  180. ctx.textAlign = 'center';
  181. ctx.textBaseline = 'middle';
  182. ctx.fillText(str, canvas.width / 2, canvas.height / 2);
  183. }
  184. draw(canvas.id);
  185. canvas.addEventListener('focus', () =&gt; {
  186. draw('has focus press a key');
  187. });
  188. canvas.addEventListener('blur', () =&gt; {
  189. draw('lost focus');
  190. });
  191. canvas.addEventListener('keydown', (e) =&gt; {
  192. draw(`keyCode: ${e.keyCode}`);
  193. });
  194. });
  195. </pre>
  196. <p>Обратите внимание, что вы не можете получить первый холст,
  197. чтобы принять ввод с клавиатуры. Второе полотно вы можете,
  198. но оно подсвечивается. На третьем холсте применяются оба решения. </p>
  199. <p></p><div translate="no" class="threejs_example_container notranslate">
  200. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-tabindex.html"></iframe></div>
  201. <a class="threejs_center" href="/manual/examples/tips-tabindex.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  202. </div>
  203. <p></p>
  204. <hr>
  205. <p><a id="transparent-canvas" data-toc="Делаем холст прозрачным "></a></p>
  206. <h1 id="-">Делаем холст прозрачным</h1>
  207. <p>По умолчанию THREE.js делает холст непрозрачным. Если вы хотите, чтобы холст был прозрачным,
  208. передайте <a href="/docs/#api/en/renderers/WebGLRenderer#alpha"><code class="notranslate" translate="no">alpha:true</code></a> при создании <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a></p>
  209. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
  210. -const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  211. +const renderer = new THREE.WebGLRenderer({
  212. + canvas,
  213. + alpha: true,
  214. +});
  215. </pre>
  216. <p>Вы, вероятно, также хотите сказать, что ваши результаты не используют предварительно умноженную альфа </p>
  217. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
  218. const renderer = new THREE.WebGLRenderer({
  219. canvas,
  220. alpha: true,
  221. + premultipliedAlpha: false,
  222. });
  223. </pre>
  224. <p>Three.js по умолчанию использует холст с использованием
  225. <a href="/docs/#api/en/renderers/WebGLRenderer#premultipliedAlpha"><code class="notranslate" translate="no">premultipliedAlpha: true</code></a> но по умолчанию используется для материалов, которые выводят
  226. <a href="/docs/#api/en/materials/Material#premultipliedAlpha"><code class="notranslate" translate="no">premultipliedAlpha: false</code></a>.</p>
  227. <p>Если вы хотите лучше понять, когда и когда не следует использовать предварительно умноженную альфу,
  228. <a href="https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre"> вот хорошая статья об этом</a>.</p>
  229. <p>В любом случае давайте настроим простой пример с прозрачным холстом. </p>
  230. <p>Мы применили настройки выше к примеру из <a href="responsive.html">статьи об отзывчивости</a>.
  231. Давайте также сделаем материалы более прозрачными</p>
  232. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
  233. - const material = new THREE.MeshPhongMaterial({color});
  234. + const material = new THREE.MeshPhongMaterial({
  235. + color,
  236. + opacity: 0.5,
  237. + });
  238. ...
  239. </pre>
  240. <p>И давайте добавим немного HTML-контента </p>
  241. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
  242. &lt;canvas id="c"&gt;&lt;/canvas&gt;
  243. + &lt;div id="content"&gt;
  244. + &lt;div&gt;
  245. + &lt;h1&gt;Cubes-R-Us!&lt;/h1&gt;
  246. + &lt;p&gt;We make the best cubes!&lt;/p&gt;
  247. + &lt;/div&gt;
  248. + &lt;/div&gt;
  249. &lt;/body&gt;
  250. </pre>
  251. <p>а также немного CSS, чтобы поставить холст впереди </p>
  252. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">body {
  253. margin: 0;
  254. }
  255. #c {
  256. width: 100%;
  257. height: 100%;
  258. display: block;
  259. + position: fixed;
  260. + left: 0;
  261. + top: 0;
  262. + z-index: 2;
  263. + pointer-events: none;
  264. }
  265. +#content {
  266. + font-size: 7vw;
  267. + font-family: sans-serif;
  268. + text-align: center;
  269. + width: 100%;
  270. + height: 100%;
  271. + display: flex;
  272. + justify-content: center;
  273. + align-items: center;
  274. +}
  275. </pre>
  276. <p>обратите внимание, что <code class="notranslate" translate="no">pointer-events: none</code> делает холст невидимым для мыши и сенсорных событий, поэтому вы можете выделить текст под ним.</p>
  277. <p></p><div translate="no" class="threejs_example_container notranslate">
  278. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-transparent-canvas.html"></iframe></div>
  279. <a class="threejs_center" href="/manual/examples/tips-transparent-canvas.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  280. </div>
  281. <p></p>
  282. <hr>
  283. <p><a id="html-background" data-toc="Создание анимированного фона в three.js "></a></p>
  284. <h1 id="-three-js">Создание анимированного фона в three.js</h1>
  285. <p>Распространенный вопрос - как сделать анимацию three.js фоном веб-страницы. </p>
  286. <p>Есть 2 очевидных способа. </p>
  287. <ul>
  288. <li>Установите у холста CSS <code class="notranslate" translate="no">position</code> <code class="notranslate" translate="no">fixed</code> как в </li>
  289. </ul>
  290. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c {
  291. position: fixed;
  292. left: 0;
  293. top: 0;
  294. ...
  295. }
  296. </pre>
  297. <p>Вы можете в основном увидеть это точное решение на предыдущем примере. Просто установите <code class="notranslate" translate="no">z-index</code> на -1, и кубы появятся за текстом. </p>
  298. <p>Небольшим недостатком этого решения является то, что ваш JavaScript должен интегрироваться со страницей,
  299. и если у вас сложная страница, вам нужно убедиться, что ни один из JavaScript в вашей визуализации
  300. three.js не конфликтует с JavaScript, выполняющим другие действия на странице. </p>
  301. <ul>
  302. <li>Используйте <code class="notranslate" translate="no">iframe</code></li>
  303. </ul>
  304. <p>Это решение используется на
  305. <a href="/">главной странице этого сайта </a>.</p>
  306. <p>На вашей веб-странице просто вставьте iframe, например </p>
  307. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;iframe id="background" src="responsive.html"&gt;
  308. &lt;div&gt;
  309. Your content goes here.
  310. &lt;/div&gt;
  311. </pre>
  312. <p>Затем создайте стиль iframe, чтобы заполнить окно и оказаться в фоновом режиме,
  313. который в основном является тем же кодом, который мы использовали выше для холста,
  314. за исключением того, что нам также нужно установить значение none, поскольку
  315. у iframes по умолчанию есть граница. </p>
  316. <pre class="prettyprint showlinemods notranslate notranslate" translate="no">#background {
  317. position: fixed;
  318. width: 100%;
  319. height: 100%;
  320. left: 0;
  321. top: 0;
  322. z-index: -1;
  323. border: none;
  324. pointer-events: none;
  325. }
  326. </pre><p></p><div translate="no" class="threejs_example_container notranslate">
  327. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-html-background.html"></iframe></div>
  328. <a class="threejs_center" href="/manual/examples/tips-html-background.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  329. </div>
  330. <p></p>
  331. </div>
  332. </div>
  333. </div>
  334. <script src="../resources/prettify.js"></script>
  335. <script src="../resources/lesson.js"></script>
  336. </body></html>