{"id":3595,"date":"2022-07-29T11:39:56","date_gmt":"2022-07-29T11:39:56","guid":{"rendered":"https:\/\/rengga.dev\/blog\/?p=3595"},"modified":"2023-06-10T11:36:14","modified_gmt":"2023-06-10T11:36:14","slug":"three-js-tutorial-three-particle-morphing-text","status":"publish","type":"post","link":"https:\/\/rengga.dev\/blog\/three-js-tutorial-three-particle-morphing-text\/","title":{"rendered":"Three JS Tutorial &#8211; Three Particle Morphing Text"},"content":{"rendered":"<p><span style=\"color: #ef3207;\"><a style=\"color: #ef3207;\" href=\"https:\/\/rengga.dev\/\" target=\"_blank\" rel=\"noopener\"><strong>Rengga Dev<\/strong><\/a> <\/span>&#8211; Particle morphing text with Three.js.<\/p>\n<p><iframe style=\"width: 100%;\" title=\"Three JS Tutorial - Three Particle Morphing Text\" src=\"https:\/\/codepen.io\/renggagumilar\/embed\/rNKVXOy?default-tab=result&amp;theme-id=dark\" height=\"800\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/renggagumilar\/pen\/rNKVXOy\"><br \/>\nThree JS Tutorial &#8211; Three Particle Morphing Text<\/a> by Rengga Gumilar (<a href=\"https:\/\/codepen.io\/renggagumilar\">@renggagumilar<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">\/\/ Options\r\nconst particleCount = 6000;\r\n        \r\nconst particleSize = .3;\r\n\r\nconst defaultAnimationSpeed = 1,\r\n        morphAnimationSpeed = 18,\r\n      \tcolor = '#FFFFFF';\r\n\r\n\/\/ Triggers\r\nconst triggers = document.getElementsByTagName('span')\r\n\r\n\/\/ Renderer\r\nvar renderer = new THREE.WebGLRenderer();\r\nrenderer.setPixelRatio(window.devicePixelRatio);\r\nrenderer.setSize( window.innerWidth, window.innerHeight );\r\ndocument.body.appendChild( renderer.domElement );\r\n\r\n\/\/ Ensure Full Screen on Resize\r\nfunction fullScreen () {\r\n    camera.aspect = window.innerWidth \/ window.innerHeight;\r\n    camera.updateProjectionMatrix();\r\n\r\n    renderer.setSize( window.innerWidth, window.innerHeight );\r\n}\r\n\r\nwindow.addEventListener('resize', fullScreen, false)\r\n\r\n\/\/ Scene\r\nvar scene = new THREE.Scene();\r\n\r\n\/\/ Camera and position\r\nvar camera = new THREE.PerspectiveCamera( 45, window.innerWidth \/ window.innerHeight, 1, 10000 );\r\n\r\ncamera.position.y = -45;\r\ncamera.position.z = 45;\r\n\r\n\/\/ Lighting\r\nvar light = new THREE.AmbientLight( 0xFFFFFF, 1 );\r\nscene.add( light );\r\n\r\n\/\/ Orbit Controls\r\nvar controls = new THREE.OrbitControls( camera );\r\ncontrols.update();\r\n\r\n\/\/ Particle Vars\r\nvar particles = new THREE.Geometry();\r\n\r\nvar texts = [];\r\n\r\nvar pMaterial = new THREE.PointCloudMaterial({\r\n            size: particleSize,\r\n});\r\n\r\n\/\/ Texts\r\nvar loader = new THREE.FontLoader();\r\nvar typeface = 'https:\/\/dl.dropboxusercontent.com\/s\/bkqic142ik0zjed\/swiss_black_cond.json?';\r\n\r\nloader.load( typeface, ( font ) =&gt; {\r\n    Array.from(triggers).forEach((trigger, idx) =&gt; {\r\n        \r\n        texts[idx] = {};\r\n        \r\n        texts[idx].geometry = new THREE.TextGeometry( trigger.textContent, {\r\n            font: font,\r\n            size: window.innerWidth * 0.02,\r\n            height: 4,\r\n            curveSegments: 10,\r\n        });\r\n        \r\n        THREE.GeometryUtils.center( texts[idx].geometry )\r\n            \r\n\r\n        texts[idx].particles = new THREE.Geometry();\r\n\r\n        texts[idx].points = THREE.GeometryUtils.randomPointsInGeometry(texts[idx].geometry, particleCount);\r\n\r\n        createVertices(texts[idx].particles, texts[idx].points)\r\n\r\n        enableTrigger(trigger, idx);\r\n        \r\n    });\r\n});\r\n\r\n\/\/ Particles\r\nfor (var p = 0; p &lt; particleCount; p++) {\r\n    var vertex = new THREE.Vector3();\r\n            vertex.x = 0;\r\n            vertex.y = 0;\r\n            vertex.z = 0;\r\n\r\n    particles.vertices.push(vertex);\r\n}\r\n\r\nfunction createVertices (emptyArray, points) {\r\n    for (var p = 0; p &lt; particleCount; p++) {\r\n        var vertex = new THREE.Vector3();\r\n                vertex.x = points[p]['x'];\r\n                vertex.y = points[p]['y'];\r\n                vertex.z = points[p]['z'];\r\n\r\n        emptyArray.vertices.push(vertex);\r\n    }\r\n}\r\n\r\nfunction enableTrigger(trigger, idx){\r\n    \r\n    \r\n    trigger.setAttribute('data-disabled', false);\r\n    \r\n    trigger.addEventListener('click', () =&gt; {\r\n        morphTo(texts[idx].particles, trigger.dataset.color);\r\n    })\r\n    \r\n    if (idx == 0) {\r\n        morphTo(texts[idx].particles, trigger.dataset.color);\r\n    }\r\n}\r\n\r\nvar particleSystem = new THREE.PointCloud(\r\n    particles,\r\n    pMaterial\r\n);\r\n\r\nparticleSystem.sortParticles = true;\r\n\r\n\/\/ Add the particles to the scene\r\nscene.add(particleSystem);\r\n\r\n\/\/ Animate\r\nconst normalSpeed = (defaultAnimationSpeed\/100),\r\n            fullSpeed = (morphAnimationSpeed\/100)\r\n\r\nlet animationVars = {\r\n    speed: normalSpeed,\r\n    color: color,\r\n    rotation: -45\r\n}\r\n\r\n\r\nfunction animate() {\r\n    \r\n    particleSystem.rotation.y += animationVars.speed;\r\n    particles.verticesNeedUpdate = true; \r\n    \r\n    camera.position.z = animationVars.rotation;\r\n    camera.position.y = animationVars.rotation;\r\n    camera.lookAt( scene.position );\r\n    \r\n    particleSystem.material.color = new THREE.Color( animationVars.color );\r\n    \r\n    window.requestAnimationFrame( animate );\r\n    renderer.render( scene, camera );\r\n}\r\n\r\nanimate();\r\n\r\nfunction morphTo (newParticles, color = '#FFFFFF') {\r\n    \r\n    TweenMax.to(animationVars, .1, {\r\n        ease: Power4.easeIn, \r\n        speed: fullSpeed, \r\n        onComplete: slowDown\r\n    });\r\n    \r\n    TweenMax.to(animationVars, 2, {\r\n        ease: Linear.easeNone, \r\n        color: color\r\n    });\r\n    \r\n    \r\n    \/\/ particleSystem.material.color.setHex(color);\r\n    \r\n    for (var i = 0; i &lt; particles.vertices.length; i++){\r\n        TweenMax.to(particles.vertices[i], 2, {\r\n            ease: Elastic.easeOut.config( 0.1, .3), \r\n            x: newParticles.vertices[i].x,\r\n            y: newParticles.vertices[i].y, \r\n            z: newParticles.vertices[i].z\r\n        })\r\n    }\r\n    \r\n    console.log(animationVars.rotation)\r\n    \r\n    TweenMax.to(animationVars, 2, {\r\n        ease: Elastic.easeOut.config( 0.1, .3), \r\n        rotation: animationVars.rotation == 45 ? -45 : 45,\r\n    })\r\n}\r\nfunction slowDown () {\r\n    TweenMax.to(animationVars, 0.3, {ease:\r\nPower2.easeOut, speed: normalSpeed, delay: 0.2});\r\n}<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rengga Dev &#8211; Particle morphing text with Three.js. See the Pen Three <a class=\"read-more\" href=\"https:\/\/rengga.dev\/blog\/three-js-tutorial-three-particle-morphing-text\/\" title=\"Three JS Tutorial &#8211; Three Particle Morphing Text\" itemprop=\"url\"><\/a><\/p>\n","protected":false},"author":1,"featured_media":3597,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20,12],"tags":[388,264,263,387,384,352,103,227],"newstopic":[],"class_list":{"0":"post-3595","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-javascript","8":"category-web-development","9":"tag-geometryutils-js","10":"tag-javascript-tutorial","11":"tag-js-tutorial","12":"tag-orbitcontrols-js","13":"tag-three-js","14":"tag-tweenmax-js","15":"tag-web-design","16":"tag-web-designer"},"_links":{"self":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3595","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/comments?post=3595"}],"version-history":[{"count":1,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3595\/revisions"}],"predecessor-version":[{"id":3596,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3595\/revisions\/3596"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media\/3597"}],"wp:attachment":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media?parent=3595"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/categories?post=3595"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/tags?post=3595"},{"taxonomy":"newstopic","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/newstopic?post=3595"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}