tips.html 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <!DOCTYPE html><html lang="en"><head>
  2. <meta charset="utf-8">
  3. <title>Tips</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 – Tips">
  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. </head>
  14. <body>
  15. <div class="container">
  16. <div class="lesson-title">
  17. <h1>Tips</h1>
  18. </div>
  19. <div class="lesson">
  20. <div class="lesson-main">
  21. <p>This article is a collection of small issues you might run into
  22. using three.js that seemed too small to have their own article.</p>
  23. <hr>
  24. <p><a id="screenshot" data-toc="Taking a screenshot"></a></p>
  25. <h1 id="taking-a-screenshot-of-the-canvas">Taking A Screenshot of the Canvas</h1>
  26. <p>In the browser there are effectively 2 functions that will take a screenshot.
  27. The old one
  28. <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL"><code class="notranslate" translate="no">canvas.toDataURL</code></a>
  29. and the new better one
  30. <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob"><code class="notranslate" translate="no">canvas.toBlob</code></a></p>
  31. <p>So you'd think it would be easy to take a screenshot by just adding some code like</p>
  32. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c"&gt;&lt;/canvas&gt;
  33. +&lt;button id="screenshot" type="button"&gt;Save...&lt;/button&gt;
  34. </pre>
  35. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const elem = document.querySelector('#screenshot');
  36. elem.addEventListener('click', () =&gt; {
  37. canvas.toBlob((blob) =&gt; {
  38. saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
  39. });
  40. });
  41. const saveBlob = (function() {
  42. const a = document.createElement('a');
  43. document.body.appendChild(a);
  44. a.style.display = 'none';
  45. return function saveData(blob, fileName) {
  46. const url = window.URL.createObjectURL(blob);
  47. a.href = url;
  48. a.download = fileName;
  49. a.click();
  50. };
  51. }());
  52. </pre>
  53. <p>Here's the example from <a href="responsive.html">the article on responsiveness</a>
  54. with the code above added and some CSS to place the button</p>
  55. <p></p><div translate="no" class="threejs_example_container notranslate">
  56. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-screenshot-bad.html"></iframe></div>
  57. <a class="threejs_center" href="/manual/examples/tips-screenshot-bad.html" target="_blank">click here to open in a separate window</a>
  58. </div>
  59. <p></p>
  60. <p>When I tried it I got this screenshot</p>
  61. <div class="threejs_center"><img src="../resources/images/screencapture-413x313.png"></div>
  62. <p>Yes, it's just a black image.</p>
  63. <p>It's possible it worked for you depending on your browser/OS but in general
  64. it's not likely to work.</p>
  65. <p>The issue is that for performance and compatibility reasons, by default the browser
  66. will clear a WebGL canvas's drawing buffer after you've drawn to it.</p>
  67. <p>The solution is to call your rendering code just before capturing.</p>
  68. <p>In our code we need to adjust a few things. First let's separate
  69. out the rendering code.</p>
  70. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const state = {
  71. + time: 0,
  72. +};
  73. -function render(time) {
  74. - time *= 0.001;
  75. +function render() {
  76. if (resizeRendererToDisplaySize(renderer)) {
  77. const canvas = renderer.domElement;
  78. camera.aspect = canvas.clientWidth / canvas.clientHeight;
  79. camera.updateProjectionMatrix();
  80. }
  81. cubes.forEach((cube, ndx) =&gt; {
  82. const speed = 1 + ndx * .1;
  83. - const rot = time * speed;
  84. + const rot = state.time * speed;
  85. cube.rotation.x = rot;
  86. cube.rotation.y = rot;
  87. });
  88. renderer.render(scene, camera);
  89. - requestAnimationFrame(render);
  90. }
  91. +function animate(time) {
  92. + state.time = time * 0.001;
  93. +
  94. + render();
  95. +
  96. + requestAnimationFrame(animate);
  97. +}
  98. +requestAnimationFrame(animate);
  99. </pre>
  100. <p>Now that <code class="notranslate" translate="no">render</code> is only concerned with actually rendering
  101. we can call it just before capturing the canvas.</p>
  102. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const elem = document.querySelector('#screenshot');
  103. elem.addEventListener('click', () =&gt; {
  104. + render();
  105. canvas.toBlob((blob) =&gt; {
  106. saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);
  107. });
  108. });
  109. </pre>
  110. <p>And now it should work.</p>
  111. <p></p><div translate="no" class="threejs_example_container notranslate">
  112. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-screenshot-good.html"></iframe></div>
  113. <a class="threejs_center" href="/manual/examples/tips-screenshot-good.html" target="_blank">click here to open in a separate window</a>
  114. </div>
  115. <p></p>
  116. <p>For a different solution see the next item.</p>
  117. <hr>
  118. <p><a id="preservedrawingbuffer" data-toc="Prevent the Canvas Being Cleared"></a></p>
  119. <h1 id="preventing-the-canvas-being-cleared">Preventing the canvas being cleared</h1>
  120. <p>Let's say you wanted to let the user paint with an animated
  121. object. You need to pass in <code class="notranslate" translate="no">preserveDrawingBuffer: true</code> when
  122. you create the <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a>. This prevents the browser from
  123. clearing the canvas. You also need to tell three.js not to clear
  124. the canvas as well.</p>
  125. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const canvas = document.querySelector('#c');
  126. -const renderer = new THREE.WebGLRenderer({canvas});
  127. +const renderer = new THREE.WebGLRenderer({
  128. + canvas,
  129. + preserveDrawingBuffer: true,
  130. + alpha: true,
  131. +});
  132. +renderer.autoClearColor = false;
  133. </pre>
  134. <p></p><div translate="no" class="threejs_example_container notranslate">
  135. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-preservedrawingbuffer.html"></iframe></div>
  136. <a class="threejs_center" href="/manual/examples/tips-preservedrawingbuffer.html" target="_blank">click here to open in a separate window</a>
  137. </div>
  138. <p></p>
  139. <p>Note that if you were serious about making a drawing program this would not be a
  140. solution as the browser will still clear the canvas anytime we change its
  141. resolution. We're changing is resolution based on its display size. Its display
  142. size changes when the window changes size. That includes when the user downloads
  143. a file, even in another tab, and the browser adds a status bar. It also includes when
  144. the user turns their phone and the browser switches from portrait to landscape.</p>
  145. <p>If you really wanted to make a drawing program you'd
  146. <a href="rendertargets.html">render to a texture using a render target</a>.</p>
  147. <hr>
  148. <p><a id="tabindex" data-toc="Get Keyboard Input From a Canvas"></a></p>
  149. <h1 id="getting-keyboard-input">Getting Keyboard Input</h1>
  150. <p>Throughout these tutorials we've often attached event listeners to the <code class="notranslate" translate="no">canvas</code>.
  151. While many events work, one that does not work by default is keyboard
  152. events.</p>
  153. <p>To get keyboard events, set the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex"><code class="notranslate" translate="no">tabindex</code></a>
  154. of the canvas to 0 or more. Eg.</p>
  155. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas tabindex="0"&gt;&lt;/canvas&gt;
  156. </pre>
  157. <p>This ends up causing a new issue though. Anything that has a <code class="notranslate" translate="no">tabindex</code> set
  158. will get highlighted when it has the focus. To fix that set its focus CSS outline
  159. to none</p>
  160. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">canvas:focus {
  161. outline:none;
  162. }
  163. </pre>
  164. <p>To demonstrate here are 3 canvases </p>
  165. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;canvas id="c1"&gt;&lt;/canvas&gt;
  166. &lt;canvas id="c2" tabindex="0"&gt;&lt;/canvas&gt;
  167. &lt;canvas id="c3" tabindex="1"&gt;&lt;/canvas&gt;
  168. </pre>
  169. <p>and some css just for the last canvas </p>
  170. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c3:focus {
  171. outline: none;
  172. }
  173. </pre>
  174. <p>Let's attach the same event listeners to all of them</p>
  175. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">document.querySelectorAll('canvas').forEach((canvas) =&gt; {
  176. const ctx = canvas.getContext('2d');
  177. function draw(str) {
  178. ctx.clearRect(0, 0, canvas.width, canvas.height);
  179. ctx.textAlign = 'center';
  180. ctx.textBaseline = 'middle';
  181. ctx.fillText(str, canvas.width / 2, canvas.height / 2);
  182. }
  183. draw(canvas.id);
  184. canvas.addEventListener('focus', () =&gt; {
  185. draw('has focus press a key');
  186. });
  187. canvas.addEventListener('blur', () =&gt; {
  188. draw('lost focus');
  189. });
  190. canvas.addEventListener('keydown', (e) =&gt; {
  191. draw(`keyCode: ${e.keyCode}`);
  192. });
  193. });
  194. </pre>
  195. <p>Notice you can't get the first canvas to accept keyboard input.
  196. The second canvas you can but it gets highlighted. The 3rd
  197. canvas has both solutions applied.</p>
  198. <p></p><div translate="no" class="threejs_example_container notranslate">
  199. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-tabindex.html"></iframe></div>
  200. <a class="threejs_center" href="/manual/examples/tips-tabindex.html" target="_blank">click here to open in a separate window</a>
  201. </div>
  202. <p></p>
  203. <hr>
  204. <p><a id="transparent-canvas" data-toc="Make the Canvas Transparent"></a></p>
  205. <h1 id="making-the-canvas-transparent">Making the Canvas Transparent</h1>
  206. <p>By default THREE.js makes the canvas opaque. If you want the
  207. canvas to be transparent pass in <a href="/docs/#api/en/renderers/WebGLRenderer#alpha"><code class="notranslate" translate="no">alpha:true</code></a> when you create
  208. the <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({canvas});
  211. +const renderer = new THREE.WebGLRenderer({
  212. + canvas,
  213. + alpha: true,
  214. +});
  215. </pre>
  216. <p>You probably also want to tell it that your results are <strong>not</strong> using premultiplied alpha</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 defaults to the canvas using
  225. <a href="/docs/#api/en/renderers/WebGLRenderer#premultipliedAlpha"><code class="notranslate" translate="no">premultipliedAlpha: true</code></a> but defaults
  226. to materials outputting <a href="/docs/#api/en/materials/Material#premultipliedAlpha"><code class="notranslate" translate="no">premultipliedAlpha: false</code></a>.</p>
  227. <p>If you'd like a better understanding of when and when not to use premultiplied alpha
  228. here's <a href="https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre">a good article on it</a>.</p>
  229. <p>In any case let's setup a simple example with a transparent canvas.</p>
  230. <p>We applied the settings above to the example from <a href="responsive.html">the article on responsiveness</a>.
  231. Let's also make the materials more transparent.</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>And let's add some HTML content</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>as well as some CSS to put the canvas in front</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>note that <code class="notranslate" translate="no">pointer-events: none</code> makes the canvas invisible to the mouse
  277. and touch events so you can select the text beneath.</p>
  278. <p></p><div translate="no" class="threejs_example_container notranslate">
  279. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-transparent-canvas.html"></iframe></div>
  280. <a class="threejs_center" href="/manual/examples/tips-transparent-canvas.html" target="_blank">click here to open in a separate window</a>
  281. </div>
  282. <p></p>
  283. <hr>
  284. <p><a id="html-background" data-toc="Use three.js as Background in HTML"></a></p>
  285. <h1 id="making-your-background-a-three-js-animation">Making your background a three.js animation</h1>
  286. <p>A common question is how to make a three.js animation be the background of
  287. a webpage.</p>
  288. <p>There are 2 obvious ways.</p>
  289. <ul>
  290. <li>Set the canvas CSS <code class="notranslate" translate="no">position</code> to <code class="notranslate" translate="no">fixed</code> as in</li>
  291. </ul>
  292. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">#c {
  293. position: fixed;
  294. left: 0;
  295. top: 0;
  296. ...
  297. }
  298. </pre>
  299. <p>You can basically see this exact solution on the previous example. Just set <code class="notranslate" translate="no">z-index</code> to -1
  300. and the cubes will appear behind the text.</p>
  301. <p>A small disadvantage to this solution is your JavaScript must integrate with the page
  302. and if you have a complex page then you need to make sure none of the JavaScript in your
  303. three.js visualization conflict with the JavaScript doing other things in the page.</p>
  304. <ul>
  305. <li>Use an <code class="notranslate" translate="no">iframe</code></li>
  306. </ul>
  307. <p>This is the solution used on <a href="/">the front page of this site</a>.</p>
  308. <p>In your webpage just insert an iframe, for example</p>
  309. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;iframe id="background" src="responsive.html"&gt;
  310. &lt;div&gt;
  311. Your content goes here.
  312. &lt;/div&gt;
  313. </pre>
  314. <p>Then style the iframe to fill the window and be in the background
  315. which is basically the same code as we used above for the canvas
  316. except we also need to set <code class="notranslate" translate="no">border</code> to <code class="notranslate" translate="no">none</code> since iframes have
  317. a border by default.</p>
  318. <pre class="prettyprint showlinemods notranslate notranslate" translate="no">#background {
  319. position: fixed;
  320. width: 100%;
  321. height: 100%;
  322. left: 0;
  323. top: 0;
  324. z-index: -1;
  325. border: none;
  326. pointer-events: none;
  327. }
  328. </pre><p></p><div translate="no" class="threejs_example_container notranslate">
  329. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/tips-html-background.html"></iframe></div>
  330. <a class="threejs_center" href="/manual/examples/tips-html-background.html" target="_blank">click here to open in a separate window</a>
  331. </div>
  332. <p></p>
  333. </div>
  334. </div>
  335. </div>
  336. <script src="/manual/resources/prettify.js"></script>
  337. <script src="/manual/resources/lesson.js"></script>
  338. </body></html>