You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
21 KiB
340 lines
21 KiB
2 years ago
|
<!DOCTYPE html><html lang="en"><head>
|
||
|
<meta charset="utf-8">
|
||
|
<title>Prerequisites</title>
|
||
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
||
|
<meta name="twitter:card" content="summary_large_image">
|
||
|
<meta name="twitter:site" content="@threejs">
|
||
|
<meta name="twitter:title" content="Three.js – Prerequisites">
|
||
|
<meta property="og:image" content="https://threejs.org/files/share.png">
|
||
|
<link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
|
||
|
<link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
|
||
|
|
||
|
<link rel="stylesheet" href="/manual/resources/lesson.css">
|
||
|
<link rel="stylesheet" href="/manual/resources/lang.css">
|
||
|
<!-- Import maps polyfill -->
|
||
|
<!-- Remove this when import maps will be widely supported -->
|
||
|
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
|
||
|
|
||
|
<script type="importmap">
|
||
|
{
|
||
|
"imports": {
|
||
|
"three": "../../build/three.module.js"
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
</head>
|
||
|
<body>
|
||
|
<div class="container">
|
||
|
<div class="lesson-title">
|
||
|
<h1>Prerequisites</h1>
|
||
|
</div>
|
||
|
<div class="lesson">
|
||
|
<div class="lesson-main">
|
||
|
<p>These articles are meant to help you learn how to use three.js.
|
||
|
They assume you know how to program in JavaScript. They assume
|
||
|
you know what the DOM is, how to write HTML as well as create DOM elements
|
||
|
in JavaScript. They assume you know how to use
|
||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">es6 modules</a>
|
||
|
via import and via <code class="notranslate" translate="no"><script type="module"></code> tags. They assume you know how to use import maps.
|
||
|
They assume you know some CSS and that you know what
|
||
|
<a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors">CSS selectors are</a>.
|
||
|
They also assume you know ES5, ES6 and maybe some ES7.
|
||
|
They assume you know that the browser runs JavaScript only via events and callbacks.
|
||
|
They assume you know what a closure is.</p>
|
||
|
<p>Here's some brief refreshers and notes</p>
|
||
|
<h2 id="es6-modules">es6 modules</h2>
|
||
|
<p>es6 modules can be loaded via the <code class="notranslate" translate="no">import</code> keyword in a script
|
||
|
or inline via a <code class="notranslate" translate="no"><script type="module"></code> tag. Here's an example</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-html" translate="no"><script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
|
||
|
|
||
|
<script type="importmap">
|
||
|
{
|
||
|
"imports": {
|
||
|
"three": "./path/to/three.module.js",
|
||
|
"three/addons/": "./different/path/to/examples/jsm/"
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<script type="module">
|
||
|
import * as THREE from 'three';
|
||
|
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
|
||
|
|
||
|
...
|
||
|
|
||
|
</script>
|
||
|
</pre>
|
||
|
<p>See more details at the bottom of <a href="fundamentals.html">this article</a>.</p>
|
||
|
<h2 id="-document-queryselector-and-document-queryselectorall-"><code class="notranslate" translate="no">document.querySelector</code> and <code class="notranslate" translate="no">document.querySelectorAll</code></h2>
|
||
|
<p>You can use <code class="notranslate" translate="no">document.querySelector</code> to select the first element
|
||
|
that matches a CSS selector. <code class="notranslate" translate="no">document.querySelectorAll</code> returns
|
||
|
all elements that match a CSS selector.</p>
|
||
|
<h2 id="you-don-t-need-onload-">You don't need <code class="notranslate" translate="no">onload</code></h2>
|
||
|
<p>Lots of 20yr old pages use HTML like</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no"><body onload="somefunction()">
|
||
|
</pre><p>That style is deprecated. Put your scripts
|
||
|
at the bottom of the page.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-html" translate="no"><html>
|
||
|
<head>
|
||
|
...
|
||
|
</head>
|
||
|
<body>
|
||
|
...
|
||
|
</body>
|
||
|
<script>
|
||
|
// inline javascript
|
||
|
</script>
|
||
|
</html>
|
||
|
</pre>
|
||
|
<p>or <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script">use the <code class="notranslate" translate="no">defer</code> property</a>.</p>
|
||
|
<h2 id="know-how-closures-work">Know how closures work</h2>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function a(v) {
|
||
|
const foo = v;
|
||
|
return function() {
|
||
|
return foo;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const f = a(123);
|
||
|
const g = a(456);
|
||
|
console.log(f()); // prints 123
|
||
|
console.log(g()); // prints 456
|
||
|
</pre>
|
||
|
<p>In the code above the function <code class="notranslate" translate="no">a</code> creates a new function every time it's called. That
|
||
|
function <em>closes</em> over the variable <code class="notranslate" translate="no">foo</code>. Here's <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures">more info</a>.</p>
|
||
|
<h2 id="understand-how-this-works">Understand how <code class="notranslate" translate="no">this</code> works</h2>
|
||
|
<p><code class="notranslate" translate="no">this</code> is not magic. It's effectively a variable that is automatically passed to functions just like
|
||
|
an argument is passed to function. The simple explanation is when you call a function directly
|
||
|
like</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no">somefunction(a, b, c);
|
||
|
</pre><p><code class="notranslate" translate="no">this</code> will be <code class="notranslate" translate="no">null</code> (when in strict mode or in a module) where as when you call a function via the dot operator <code class="notranslate" translate="no">.</code> like this</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no">someobject.somefunction(a, b, c);
|
||
|
</pre><p><code class="notranslate" translate="no">this</code> will be set to <code class="notranslate" translate="no">someobject</code>.</p>
|
||
|
<p>The parts where people get confused is with callbacks.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const callback = someobject.somefunction;
|
||
|
loader.load(callback);
|
||
|
</pre><p>doesn't work as someone inexperienced might expect because when
|
||
|
<code class="notranslate" translate="no">loader.load</code> calls the callback it's not calling it with the dot <code class="notranslate" translate="no">.</code> operator
|
||
|
so by default <code class="notranslate" translate="no">this</code> will be null (unless the loader explicitly sets it to something).
|
||
|
If you want <code class="notranslate" translate="no">this</code> to be <code class="notranslate" translate="no">someobject</code> when the callback happens you need to
|
||
|
tell JavaScript that by binding it to the function.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no"> const callback = someobject.somefunction.bind(someobject);
|
||
|
loader.load(callback);
|
||
|
</pre><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this"><em>this</em> article might help explain <code class="notranslate" translate="no">this</code></a>.</p>
|
||
|
<h2 id="es5-es6-es7-stuff">ES5/ES6/ES7 stuff</h2>
|
||
|
<h3 id="-var-is-deprecated-use-const-and-or-let-"><code class="notranslate" translate="no">var</code> is deprecated. Use <code class="notranslate" translate="no">const</code> and/or <code class="notranslate" translate="no">let</code></h3>
|
||
|
<p>There is no reason to use <code class="notranslate" translate="no">var</code> <strong>EVER</strong> and at this point it's considered bad practice
|
||
|
to use it at all. Use <code class="notranslate" translate="no">const</code> if the variable will never be reassigned which is most of
|
||
|
the time. Use <code class="notranslate" translate="no">let</code> in those cases where the value changes. This will help avoid tons of bugs.</p>
|
||
|
<h3 id="use-for-elem-of-collection-never-for-elem-in-collection-">Use <code class="notranslate" translate="no">for(elem of collection)</code> never <code class="notranslate" translate="no">for(elem in collection)</code></h3>
|
||
|
<p><code class="notranslate" translate="no">for of</code> is new, <code class="notranslate" translate="no">for in</code> is old. <code class="notranslate" translate="no">for in</code> had issues that are solved by <code class="notranslate" translate="no">for of</code></p>
|
||
|
<p>As one example you can iterate over all the key/value pairs of an object with</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">for (const [key, value] of Object.entries(someObject)) {
|
||
|
console.log(key, value);
|
||
|
}
|
||
|
</pre>
|
||
|
<h3 id="use-foreach-map-and-filter-where-useful">Use <code class="notranslate" translate="no">forEach</code>, <code class="notranslate" translate="no">map</code>, and <code class="notranslate" translate="no">filter</code> where useful</h3>
|
||
|
<p>Arrays added the functions <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach"><code class="notranslate" translate="no">forEach</code></a>,
|
||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code class="notranslate" translate="no">map</code></a>, and
|
||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter"><code class="notranslate" translate="no">filter</code></a> and
|
||
|
are used fairly extensively in modern JavaScript.</p>
|
||
|
<h3 id="use-destructuring">Use destructuring</h3>
|
||
|
<p>Assume an object <code class="notranslate" translate="no">const dims = {width: 300, height: 150}</code></p>
|
||
|
<p>old code</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const width = dims.width;
|
||
|
const height = dims.height;
|
||
|
</pre>
|
||
|
<p>new code</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const {width, height} = dims;
|
||
|
</pre>
|
||
|
<p>Destructuring works with arrays too. Assume an array <code class="notranslate" translate="no">const position = [5, 6, 7, 1]</code>;</p>
|
||
|
<p>old code</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const y = position[1];
|
||
|
const z = position[2];
|
||
|
</pre>
|
||
|
<p>new code</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const [, y, z] = position;
|
||
|
</pre>
|
||
|
<p>Destructuring also works in function arguments</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const dims = {width: 300, height: 150};
|
||
|
const vector = [3, 4];
|
||
|
|
||
|
function lengthOfVector([x, y]) {
|
||
|
return Math.sqrt(x * x + y * y);
|
||
|
}
|
||
|
|
||
|
const dist = lengthOfVector(vector); // dist = 5
|
||
|
|
||
|
function area({width, height}) {
|
||
|
return width * height;
|
||
|
}
|
||
|
const a = area(dims); // a = 45000
|
||
|
</pre>
|
||
|
<h3 id="use-object-declaration-short-cuts">Use object declaration short cuts</h3>
|
||
|
<p>old code</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const width = 300;
|
||
|
const height = 150;
|
||
|
const obj = {
|
||
|
width: width,
|
||
|
height: height,
|
||
|
area: function() {
|
||
|
return this.width * this.height
|
||
|
},
|
||
|
};
|
||
|
</pre>
|
||
|
<p>new code</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no"> const width = 300;
|
||
|
const height = 150;
|
||
|
const obj = {
|
||
|
width,
|
||
|
height,
|
||
|
area() {
|
||
|
return this.width * this.height;
|
||
|
},
|
||
|
};
|
||
|
</pre>
|
||
|
<h3 id="use-the-rest-parameter-and-the-spread-operator-">Use the rest parameter and the spread operator <code class="notranslate" translate="no">...</code></h3>
|
||
|
<p>The rest parameter can be used to consume any number of parameters. Example</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no"> function log(className, ...args) {
|
||
|
const elem = document.createElement('div');
|
||
|
elem.className = className;
|
||
|
elem.textContent = args.join(' ');
|
||
|
document.body.appendChild(elem);
|
||
|
}
|
||
|
</pre>
|
||
|
<p>The spread operator can be used to expand an iterable into arguments</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const position = [1, 2, 3];
|
||
|
someMesh.position.set(...position);
|
||
|
</pre>
|
||
|
<p>or copy an array</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const copiedPositionArray = [...position];
|
||
|
copiedPositionArray.push(4); // [1,2,3,4]
|
||
|
console.log(position); // [1,2,3] position is unaffected
|
||
|
</pre>
|
||
|
<p>or to merge objects</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no">const a = {abc: 123};
|
||
|
const b = {def: 456};
|
||
|
const c = {...a, ...b}; // c is now {abc: 123, def: 456}
|
||
|
</pre><h3 id="use-class-">Use <code class="notranslate" translate="no">class</code></h3>
|
||
|
<p>The syntax for making class like objects pre ES5 was unfamiliar to most
|
||
|
programmers. As of ES5 you can now <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">use the <code class="notranslate" translate="no">class</code>
|
||
|
keyword</a>
|
||
|
which is closer to the style of C++/C#/Java.</p>
|
||
|
<h3 id="understand-getters-and-setters">Understand getters and setters</h3>
|
||
|
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get">Getters</a> and
|
||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set">setters</a> are
|
||
|
common in most modern languages. The <code class="notranslate" translate="no">class</code> syntax
|
||
|
of ES5 makes them much easier than pre ES5.</p>
|
||
|
<h3 id="use-arrow-functions-where-appropriate">Use arrow functions where appropriate</h3>
|
||
|
<p>This is especially useful with callbacks and promises.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">loader.load((texture) => {
|
||
|
// use texture
|
||
|
});
|
||
|
</pre>
|
||
|
<p>Arrow functions bind <code class="notranslate" translate="no">this</code> to the context in which you create the arrow function.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = (args) => {/* code */};
|
||
|
</pre>
|
||
|
<p>is a shortcut for</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = (function(args) {/* code */}).bind(this));
|
||
|
</pre>
|
||
|
<p>See link above for more info on <code class="notranslate" translate="no">this</code>.</p>
|
||
|
<h3 id="promises-as-well-as-async-await">Promises as well as async/await</h3>
|
||
|
<p>Promises help with asynchronous code. Async/await help
|
||
|
use promises.</p>
|
||
|
<p>It's too big a topic to go into here but you can <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">read up
|
||
|
on promises here</a>
|
||
|
and <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async/await here</a>.</p>
|
||
|
<h3 id="use-template-literals">Use Template Literals</h3>
|
||
|
<p>Template literals are strings using backticks instead of quotes.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate notranslate" translate="no">const foo = `this is a template literal`;
|
||
|
</pre><p>Template literals have basically 2 features. One is they can be multi-line</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const foo = `this
|
||
|
is
|
||
|
a
|
||
|
template
|
||
|
literal`;
|
||
|
const bar = "this\nis\na\ntemplate\nliteral";
|
||
|
</pre>
|
||
|
<p><code class="notranslate" translate="no">foo</code> and <code class="notranslate" translate="no">bar</code> above are the same.</p>
|
||
|
<p>The other is that you can pop out of string mode and insert snippets of
|
||
|
JavaScript using <code class="notranslate" translate="no">${javascript-expression}</code>. This is the template part. Example:</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const r = 192;
|
||
|
const g = 255;
|
||
|
const b = 64;
|
||
|
const rgbCSSColor = `rgb(${r},${g},${b})`;
|
||
|
</pre>
|
||
|
<p>or</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const color = [192, 255, 64];
|
||
|
const rgbCSSColor = `rgb(${color.join(',')})`;
|
||
|
</pre>
|
||
|
<p>or</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const aWidth = 10;
|
||
|
const bWidth = 20;
|
||
|
someElement.style.width = `${aWidth + bWidth}px`;
|
||
|
</pre>
|
||
|
<h1 id="learn-javascript-coding-conventions-">Learn JavaScript coding conventions.</h1>
|
||
|
<p>While you're welcome to format your code any way you chose there is at least one
|
||
|
convention you should be aware of. Variables, function names, method names, in
|
||
|
JavaScript are all lowerCasedCamelCase. Constructors, the names of classes are
|
||
|
CapitalizedCamelCase. If you follow this rule you code will match most other
|
||
|
JavaScript. Many <a href="https://eslint.org">linters</a>, programs that check for obvious errors in your code,
|
||
|
will point out errors if you use the wrong case since by following the convention
|
||
|
above they can know when you're using something incorrectly.</p>
|
||
|
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const v = new vector(); // clearly an error if all classes start with a capital letter
|
||
|
const v = Vector(); // clearly an error if all functions start with a lowercase latter.
|
||
|
</pre>
|
||
|
<h1 id="consider-using-visual-studio-code">Consider using Visual Studio Code</h1>
|
||
|
<p>Of course use whatever editor you want but if you haven't tried it consider
|
||
|
using <a href="https://code.visualstudio.com/">Visual Studio Code</a> for JavaScript and
|
||
|
after installing it <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">setup
|
||
|
eslint</a>.
|
||
|
It might take a few minutes to setup but it will help you immensely with finding
|
||
|
bugs in your JavaScript.</p>
|
||
|
<p>Some examples</p>
|
||
|
<p>If you enable <a href="https://eslint.org/docs/rules/no-undef">the <code class="notranslate" translate="no">no-undef</code> rule</a> then
|
||
|
VSCode via ESLint will warn you of many undefined variables. </p>
|
||
|
<div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-not-defined.png"></div>
|
||
|
|
||
|
<p>Above you can see I mis-spelled <code class="notranslate" translate="no">doTheThing</code> as <code class="notranslate" translate="no">doThing</code>. There's a red squiggle
|
||
|
under <code class="notranslate" translate="no">doThing</code> and hovering over it it tells me it's undefined. One error
|
||
|
avoided.</p>
|
||
|
<p>If you're using <code class="notranslate" translate="no"><script></code> tags to include three.js you'll get warnings using <code class="notranslate" translate="no">THREE</code> so add <code class="notranslate" translate="no">/* global THREE */</code> at the top of your
|
||
|
JavaScript files to tell eslint that <code class="notranslate" translate="no">THREE</code> exists. (or better, use <code class="notranslate" translate="no">import</code> 😉)</p>
|
||
|
<div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-not-a-constructor.png"></div>
|
||
|
|
||
|
<p>Above you can see eslint knows the rule that <code class="notranslate" translate="no">UpperCaseNames</code> are constructors
|
||
|
and so you should be using <code class="notranslate" translate="no">new</code>. Another error caught and avoided. This is <a href="https://eslint.org/docs/rules/new-cap">the
|
||
|
<code class="notranslate" translate="no">new-cap</code> rule</a>.</p>
|
||
|
<p>There are <a href="https://eslint.org/docs/rules/">100s of rules you can turn on or off or
|
||
|
customize</a>. For example above I mentioned you
|
||
|
should use <code class="notranslate" translate="no">const</code> and <code class="notranslate" translate="no">let</code> over <code class="notranslate" translate="no">var</code>.</p>
|
||
|
<p>Here I used <code class="notranslate" translate="no">var</code> and it warned me I should use <code class="notranslate" translate="no">let</code> or <code class="notranslate" translate="no">const</code></p>
|
||
|
<div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-var.png"></div>
|
||
|
|
||
|
<p>Here I used <code class="notranslate" translate="no">let</code> but it saw I never change the value so it suggested I use <code class="notranslate" translate="no">const</code>.</p>
|
||
|
<div class="threejs_center"><img style="width: 615px;" src="../resources/images/vscode-eslint-let.png"></div>
|
||
|
|
||
|
<p>Of course if you'd prefer to keep using <code class="notranslate" translate="no">var</code> you can just turn off that rule.
|
||
|
As I said above though I prefer to use <code class="notranslate" translate="no">const</code> and <code class="notranslate" translate="no">let</code> over <code class="notranslate" translate="no">var</code> as they just
|
||
|
work better and prevent bugs.</p>
|
||
|
<p>For those cases where you really need to override a rule <a href="https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments">you can add comments
|
||
|
to disable
|
||
|
them</a>
|
||
|
for a single line or a section of code.</p>
|
||
|
<h1 id="if-you-really-need-to-support-legacy-browsers-use-a-transpiler">If you really need to support legacy browsers use a transpiler</h1>
|
||
|
<p>Most modern browsers are auto-updated so using all these features will help you
|
||
|
be productive and avoid bugs. That said, if you're on a project that absolutely
|
||
|
must support old browsers there are <a href="https://babeljs.io">tools that will take your ES5/ES6/ES7 code
|
||
|
and transpile the code back to pre ES5 Javascript</a>.</p>
|
||
|
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<script src="/manual/resources/prettify.js"></script>
|
||
|
<script src="/manual/resources/lesson.js"></script>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
</body></html>
|