flaotfont.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. "use strict";
  2. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  3. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  4. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  5. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  6. console.clear();
  7. var TWOPI = Math.PI * 2;
  8. function distance(x1, y1, x2, y2) {
  9. var dx = x1 - x2;
  10. var dy = y1 - y2;
  11. return Math.sqrt(dx * dx + dy * dy);
  12. }
  13. var gravity = 0.5;
  14. // VNode class
  15. var VNode = function () {
  16. function VNode(node) {
  17. _classCallCheck(this, VNode);
  18. this.x = node.x || 0;
  19. this.y = node.y || 0;
  20. this.oldX = this.x;
  21. this.oldY = this.y;
  22. this.w = node.w || 2;
  23. this.angle = node.angle || 0;
  24. this.gravity = node.gravity || gravity;
  25. this.mass = node.mass || 1.0;
  26. this.color = node.color;
  27. this.letter = node.letter;
  28. this.pointerMove = node.pointerMove;
  29. this.fixed = node.fixed;
  30. }
  31. // verlet integration
  32. _createClass(VNode, [{
  33. key: 'integrate',
  34. value: function integrate(pointer) {
  35. if (this.lock && (!this.lockX || !this.lockY)) {
  36. this.lockX = this.x;
  37. this.lockY = this.y;
  38. }
  39. if (this.pointerMove && pointer && distance(this.x, this.y, pointer.x, pointer.y) < this.w + pointer.w) {
  40. this.x += (pointer.x - this.x) / (this.mass * 18);
  41. this.y += (pointer.y - this.y) / (this.mass * 18);
  42. } else if (this.lock) {
  43. this.x += (this.lockX - this.x) * this.lock;
  44. this.y += (this.lockY - this.y) * this.lock;
  45. }
  46. if (!this.fixed) {
  47. var x = this.x;
  48. var y = this.y;
  49. this.x += this.x - this.oldX;
  50. this.y += this.y - this.oldY + this.gravity;
  51. this.oldX = x;
  52. this.oldY = y;
  53. }
  54. }
  55. }, {
  56. key: 'set',
  57. value: function set(x, y) {
  58. this.oldX = this.x = x;
  59. this.oldY = this.y = y;
  60. }
  61. // draw node
  62. }, {
  63. key: 'draw',
  64. value: function draw(ctx) {
  65. if (!this.color) {
  66. return;
  67. }
  68. // ctx.globalAlpha = 0.8;
  69. ctx.translate(this.x, this.y);
  70. ctx.rotate(this.angle);
  71. ctx.fillStyle = this.color;
  72. ctx.beginPath();
  73. if (this.letter) {
  74. ctx.globalAlpha = 1;
  75. ctx.rotate(Math.PI / 2);
  76. ctx.rect(-7, 0, 14, 1);
  77. ctx.textAlign = 'center';
  78. ctx.textBaseline = 'middle';
  79. ctx.font = 'bold 75px "Bebas Neue", monospace';
  80. ctx.fillStyle = '#000';
  81. ctx.fillText(this.letter, 0, this.w * .25 + 4);
  82. ctx.fillStyle = this.color;
  83. ctx.fillText(this.letter, 0, this.w * .25);
  84. } else {
  85. ctx.globalAlpha = 0.2;
  86. ctx.rect(-this.w, -this.w, this.w * 2, this.w * 2);
  87. // ctx.arc(this.x, this.y, this.w, 0, 2 * Math.PI);
  88. }
  89. ctx.closePath();
  90. ctx.fill();
  91. ctx.setTransform(1, 0, 0, 1, 0, 0);
  92. }
  93. }]);
  94. return VNode;
  95. }();
  96. // constraint class
  97. var Constraint = function () {
  98. function Constraint(n0, n1, stiffness) {
  99. _classCallCheck(this, Constraint);
  100. this.n0 = n0;
  101. this.n1 = n1;
  102. this.dist = distance(n0.x, n0.y, n1.x, n1.y);
  103. this.stiffness = stiffness || 0.5;
  104. this.firstRun = true;
  105. }
  106. // solve constraint
  107. _createClass(Constraint, [{
  108. key: 'solve',
  109. value: function solve() {
  110. var dx = this.n0.x - this.n1.x;
  111. var dy = this.n0.y - this.n1.y;
  112. var newAngle = Math.atan2(dy, dx);
  113. this.n1.angle = newAngle;
  114. var currentDist = distance(this.n0.x, this.n0.y, this.n1.x, this.n1.y);
  115. var delta = this.stiffness * (currentDist - this.dist) / currentDist;
  116. dx *= delta;
  117. dy *= delta;
  118. if (this.firstRun) {
  119. this.firstRun = false;
  120. if (!this.n1.fixed) {
  121. this.n1.x += dx;
  122. this.n1.y += dy;
  123. }
  124. if (!this.n0.fixed) {
  125. this.n0.x -= dx;
  126. this.n0.y -= dy;
  127. }
  128. return;
  129. }
  130. var m1 = this.n0.mass + this.n1.mass;
  131. var m2 = this.n0.mass / m1;
  132. m1 = this.n1.mass / m1;
  133. if (!this.n1.fixed) {
  134. this.n1.x += dx * m2;
  135. this.n1.y += dy * m2;
  136. }
  137. if (!this.n0.fixed) {
  138. this.n0.x -= dx * m1;
  139. this.n0.y -= dy * m1;
  140. }
  141. }
  142. // draw constraint
  143. }, {
  144. key: 'draw',
  145. value: function draw(ctx) {
  146. ctx.globalAlpha = 0.9;
  147. ctx.beginPath();
  148. ctx.moveTo(this.n0.x, this.n0.y);
  149. ctx.lineTo(this.n1.x, this.n1.y);
  150. ctx.strokeStyle = "#fff";
  151. ctx.stroke();
  152. }
  153. }]);
  154. return Constraint;
  155. }();
  156. var Rope = function () {
  157. function Rope(rope) {
  158. _classCallCheck(this, Rope);
  159. var x = rope.x,
  160. y = rope.y,
  161. length = rope.length,
  162. points = rope.points,
  163. vertical = rope.vertical,
  164. fixedEnds = rope.fixedEnds,
  165. startNode = rope.startNode,
  166. letter = rope.letter,
  167. endNode = rope.endNode,
  168. stiffness = rope.stiffness,
  169. constrain = rope.constrain,
  170. gravity = rope.gravity,
  171. pointerMove = rope.pointerMove;
  172. this.stiffness = stiffness || 1;
  173. this.nodes = [];
  174. this.constraints = [];
  175. if (letter === ' ') {
  176. return this;
  177. }
  178. var dist = length / points;
  179. for (var i = 0, _last = points - 1; i < points; i++) {
  180. var size = letter && i === _last ? 15 : 2;
  181. var spacing = dist * i + size;
  182. var node = new VNode({
  183. w: size,
  184. mass: .1, //(i === last ? .5 : .1),
  185. fixed: fixedEnds && (i === 0 || i === _last)
  186. });
  187. node = i === 0 && startNode || i === _last && endNode || node;
  188. node.gravity = gravity;
  189. //node.pointerMove = pointerMove;
  190. if (i === _last && letter) {
  191. node.letter = letter;
  192. node.color = '#FFF';
  193. node.pointerMove = true;
  194. }
  195. node.oldX = node.x = x + (!vertical ? spacing : 0);
  196. node.oldY = node.y = y + (vertical ? spacing : 0);
  197. this.nodes.push(node);
  198. }
  199. constrain ? this.makeConstraints() : null;
  200. }
  201. _createClass(Rope, [{
  202. key: 'makeConstraints',
  203. value: function makeConstraints() {
  204. for (var i = 1; i < this.nodes.length; i++) {
  205. this.constraints.push(new Constraint(this.nodes[i - 1], this.nodes[i], this.stiffness));
  206. }
  207. }
  208. }, {
  209. key: 'run',
  210. value: function run(pointer) {
  211. // integration
  212. var _iteratorNormalCompletion = true;
  213. var _didIteratorError = false;
  214. var _iteratorError = undefined;
  215. try {
  216. for (var _iterator = this.nodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  217. var n = _step.value;
  218. n.integrate(pointer);
  219. }
  220. // solve constraints
  221. } catch (err) {
  222. _didIteratorError = true;
  223. _iteratorError = err;
  224. } finally {
  225. try {
  226. if (!_iteratorNormalCompletion && _iterator.return) {
  227. _iterator.return();
  228. }
  229. } finally {
  230. if (_didIteratorError) {
  231. throw _iteratorError;
  232. }
  233. }
  234. }
  235. for (var i = 0; i < 5; i++) {
  236. var _iteratorNormalCompletion2 = true;
  237. var _didIteratorError2 = false;
  238. var _iteratorError2 = undefined;
  239. try {
  240. for (var _iterator2 = this.constraints[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
  241. var _n = _step2.value;
  242. _n.solve();
  243. }
  244. } catch (err) {
  245. _didIteratorError2 = true;
  246. _iteratorError2 = err;
  247. } finally {
  248. try {
  249. if (!_iteratorNormalCompletion2 && _iterator2.return) {
  250. _iterator2.return();
  251. }
  252. } finally {
  253. if (_didIteratorError2) {
  254. throw _iteratorError2;
  255. }
  256. }
  257. }
  258. }
  259. }
  260. // draw(ctx) {
  261. // // draw constraints
  262. // this.constraints.forEach(n => {
  263. // n.draw(ctx);
  264. // });
  265. // // draw nodes
  266. // this.nodes.forEach(n => {
  267. // n.draw(ctx);
  268. // })
  269. // }
  270. }, {
  271. key: 'draw',
  272. value: function draw(ctx) {
  273. var vertices = Array.from(this.constraints).reduce(function (p, c, i, a) {
  274. p.push(c.n0);
  275. if (i == a.length - 1) p.push(c.n1);
  276. return p;
  277. }, []);
  278. var h = function h(x, y) {
  279. return Math.sqrt(x * x + y * y);
  280. };
  281. var tension = 0.5;
  282. if (!vertices.length) return;
  283. var controls = vertices.map(function () {
  284. return null;
  285. });
  286. for (var i = 1; i < vertices.length - 1; ++i) {
  287. var previous = vertices[i - 1];
  288. var current = vertices[i];
  289. var next = vertices[i + 1];
  290. var rdx = next.x - previous.x,
  291. rdy = next.y - previous.y,
  292. rd = h(rdx, rdy),
  293. dx = rdx / rd,
  294. dy = rdy / rd;
  295. var dp = h(current.x - previous.x, current.y - previous.y),
  296. dn = h(current.x - next.x, current.y - next.y);
  297. var cpx = current.x - dx * dp * tension,
  298. cpy = current.y - dy * dp * tension,
  299. cnx = current.x + dx * dn * tension,
  300. cny = current.y + dy * dn * tension;
  301. controls[i] = {
  302. cp: {
  303. x: cpx,
  304. y: cpy
  305. },
  306. cn: {
  307. x: cnx,
  308. y: cny
  309. }
  310. };
  311. }
  312. controls[0] = {
  313. cn: {
  314. x: (vertices[0].x + controls[1].cp.x) / 2,
  315. y: (vertices[0].y + controls[1].cp.y) / 2
  316. }
  317. };
  318. controls[vertices.length - 1] = {
  319. cp: {
  320. x: (vertices[vertices.length - 1].x + controls[vertices.length - 2].cn.x) / 2,
  321. y: (vertices[vertices.length - 1].y + controls[vertices.length - 2].cn.y) / 2
  322. }
  323. };
  324. // Draw vertices & control points
  325. // ctx.fillStyle = 'blue';
  326. // ctx.fillRect(vertices[0].x, vertices[0].y, 4, 4);
  327. // for (let i = 1; i < vertices.length; ++i)
  328. // {
  329. // const v = vertices[i];
  330. // const ca = controls[i - 1];
  331. // const cb = controls[i];
  332. // ctx.fillStyle = 'blue';
  333. // ctx.fillRect(v.x, v.y, 4, 4);
  334. // ctx.fillStyle = 'green';
  335. // ctx.fillRect(ca.cn.x, ca.cn.y, 4, 4);
  336. // ctx.fillRect(cb.cp.x, cb.cp.y, 4, 4);
  337. // }
  338. ctx.globalAlpha = 0.9;
  339. ctx.beginPath();
  340. ctx.moveTo(vertices[0].x, vertices[0].y);
  341. for (var _i = 1; _i < vertices.length; ++_i) {
  342. var v = vertices[_i];
  343. var ca = controls[_i - 1];
  344. var cb = controls[_i];
  345. ctx.bezierCurveTo(ca.cn.x, ca.cn.y, cb.cp.x, cb.cp.y, v.x, v.y);
  346. }
  347. ctx.strokeStyle = 'white';
  348. ctx.stroke();
  349. ctx.closePath();
  350. // draw nodes
  351. this.nodes.forEach(function (n) {
  352. n.draw(ctx);
  353. });
  354. }
  355. }]);
  356. return Rope;
  357. }();
  358. // Pointer class
  359. var Pointer = function (_VNode) {
  360. _inherits(Pointer, _VNode);
  361. function Pointer(canvas) {
  362. _classCallCheck(this, Pointer);
  363. var _this = _possibleConstructorReturn(this, (Pointer.__proto__ || Object.getPrototypeOf(Pointer)).call(this, {
  364. x: 0,
  365. y: 0,
  366. w: 8,
  367. color: '#F00',
  368. fixed: true
  369. }));
  370. _this.elem = canvas;
  371. canvas.addEventListener("mousemove", function (e) {
  372. return _this.move(e);
  373. }, false);
  374. canvas.addEventListener("touchmove", function (e) {
  375. return _this.move(e);
  376. }, false);
  377. return _this;
  378. }
  379. _createClass(Pointer, [{
  380. key: 'move',
  381. value: function move(e) {
  382. var touchMode = e.targetTouches;
  383. var pointer = e;
  384. if (touchMode) {
  385. e.preventDefault();
  386. pointer = touchMode[0];
  387. }
  388. var rect = this.elem.getBoundingClientRect();
  389. var cw = this.elem.width;
  390. var ch = this.elem.height;
  391. // get the scale based on actual width;
  392. var sx = cw / this.elem.offsetWidth;
  393. var sy = ch / this.elem.offsetHeight;
  394. this.x = (pointer.clientX - rect.left) * sx;
  395. this.y = (pointer.clientY - rect.top) * sy;
  396. }
  397. }]);
  398. return Pointer;
  399. }(VNode);
  400. var Scene = function () {
  401. function Scene(canvas) {
  402. _classCallCheck(this, Scene);
  403. this.draw = true;
  404. this.canvas = canvas;
  405. this.ctx = canvas.getContext('2d');
  406. this.nodes = new Set();
  407. this.constraints = new Set();
  408. this.ropes = [];
  409. this.pointer = new Pointer(canvas);
  410. this.nodes.add(this.pointer);
  411. this.run = this.run.bind(this);
  412. this.addRope = this.addRope.bind(this);
  413. this.add = this.add.bind(this);
  414. }
  415. // animation loop
  416. _createClass(Scene, [{
  417. key: 'run',
  418. value: function run() {
  419. var _this2 = this;
  420. // if (!canvas.isConnected) {
  421. // return;
  422. // }
  423. requestAnimationFrame(this.run);
  424. // clear screen
  425. this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  426. this.ropes.forEach(function (rope) {
  427. rope.run(_this2.pointer);
  428. });
  429. this.ropes.forEach(function (rope) {
  430. rope.draw(_this2.ctx);
  431. });
  432. // // integration
  433. // for (let n of nodes) {
  434. // n.integrate(pointer);
  435. // }
  436. // solve constraints
  437. // for (let i = 0; i < 4; i++) {
  438. // for (let n of constraints) {
  439. // n.solve();
  440. // }
  441. // }
  442. // // draw constraints
  443. // for (let n of constraints) {
  444. // n.draw(ctx);
  445. // }
  446. // draw nodes
  447. var _iteratorNormalCompletion3 = true;
  448. var _didIteratorError3 = false;
  449. var _iteratorError3 = undefined;
  450. try {
  451. for (var _iterator3 = this.nodes[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
  452. var n = _step3.value;
  453. n.draw(this.ctx);
  454. }
  455. } catch (err) {
  456. _didIteratorError3 = true;
  457. _iteratorError3 = err;
  458. } finally {
  459. try {
  460. if (!_iteratorNormalCompletion3 && _iterator3.return) {
  461. _iterator3.return();
  462. }
  463. } finally {
  464. if (_didIteratorError3) {
  465. throw _iteratorError3;
  466. }
  467. }
  468. }
  469. }
  470. }, {
  471. key: 'addRope',
  472. value: function addRope(rope) {
  473. this.ropes.push(rope);
  474. }
  475. }, {
  476. key: 'add',
  477. value: function add(struct) {
  478. // load nodes
  479. for (var n in struct.nodes) {
  480. this.nodes.add(struct.nodes[n]);
  481. /*
  482. const node = new Node(struct.nodes[n]);
  483. struct.nodes[n].id = node;
  484. nodes.add(node);
  485. */
  486. }
  487. // load constraints
  488. for (var i = 0; i < struct.constraints.length; i++) {
  489. var c = struct.constraints[i];
  490. this.constraints.add(c);
  491. /*
  492. new Constraint(
  493. struct.nodes[c[0]].id,
  494. struct.nodes[c[1]].id
  495. )
  496. );
  497. */
  498. }
  499. }
  500. }]);
  501. return Scene;
  502. }();
  503. var scene = new Scene(document.querySelector('#canvas'));
  504. scene.run();
  505. // const pointer = new Pointer(canvas);
  506. var phrase = ' CRMEB ';
  507. var r = new Rope({
  508. x: scene.canvas.width * 0.15,
  509. y: 40,
  510. length: scene.canvas.width * 0.7,
  511. points: phrase.length,
  512. vertical: false,
  513. dangleEnd: false,
  514. fixedEnds: true,
  515. stiffness: 1.5,
  516. constrain: false,
  517. gravity: 0.1
  518. });
  519. var center = r.nodes.length / 2;
  520. var ropes = r.nodes.map(function (n, i) {
  521. n.set(n.x, 60 + 80 * (1 - Math.abs((center - i) % center / center)));
  522. if (phrase[i] !== ' ') {
  523. //if ( i !== 0 && i !== r.nodes.length - 1 ) {
  524. return new Rope({
  525. startNode: n,
  526. x: n.x,
  527. y: n.y,
  528. length: 60,
  529. points: 4,
  530. letter: phrase[i],
  531. vertical: true,
  532. stiffness: 1, //2.5,,
  533. constrain: false,
  534. gravity: 0.5
  535. });
  536. }
  537. //}
  538. });
  539. var first = r.nodes[0];
  540. var last = r.nodes[r.nodes.length - 1];
  541. first.set(2, -2);
  542. last.set(scene.canvas.width - 2, -2);
  543. r.makeConstraints();
  544. ropes = ropes;
  545. scene.addRope(r);
  546. ropes.filter(function (r) {
  547. return r;
  548. }).forEach(function (r) {
  549. r.makeConstraints();
  550. scene.addRope(r);
  551. });