tips.html 20 KB

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