{"id":1208,"date":"2023-04-08T03:49:54","date_gmt":"2023-04-08T00:49:54","guid":{"rendered":"https:\/\/notes.profidev.online\/?p=1208"},"modified":"2023-04-08T03:49:56","modified_gmt":"2023-04-08T00:49:56","slug":"kinetic-typography","status":"publish","type":"post","link":"https:\/\/notes.profidev.online\/ru\/elements\/1208\/","title":{"rendered":"Kinetic Typography"},"content":{"rendered":"\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"Kinetic Typography\" width=\"640\" height=\"360\" src=\"https:\/\/www.youtube.com\/embed\/w3dT9OFUyBI?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;htmlmixed&quot;,&quot;mime&quot;:&quot;text\/html&quot;,&quot;theme&quot;:&quot;cobalt&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;HTML&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;html&quot;}\">  &lt;canvas id=&quot;Canvas&quot;&gt;&lt;\/canvas&gt;\n\n  &lt;script src=&quot;https:\/\/s3-us-west-2.amazonaws.com\/s.cdpn.io\/16327\/gsap-latest-beta.min.js&quot;&gt;&lt;\/script&gt;\n  &lt;script src=&quot;https:\/\/s3-us-west-2.amazonaws.com\/s.cdpn.io\/16327\/EasePack3.min.js&quot;&gt;&lt;\/script&gt;\n  &lt;script src=&quot;https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/109\/three.min.js&quot;&gt;&lt;\/script&gt;\n  &lt;script src=&quot;https:\/\/ajax.googleapis.com\/ajax\/libs\/webfont\/1.6.26\/webfont.js&quot;&gt;&lt;\/script&gt;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;css&quot;,&quot;mime&quot;:&quot;text\/css&quot;,&quot;theme&quot;:&quot;cobalt&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;CSS&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;css&quot;}\">* {\n  margin: 0;\n  padding: 0;\n}\n\nhtml {\n  background-color: #245ded;\n  font-family: 'Muli', sans-serif;\n  color: #fff;\n}\n\n \n#Canvas {\n  display: block;\n  position: absolute;\n  top: calc(50% - 270px);\n  left: calc(50% - 480px);\n  width: 960px;\n  height: 540px;\n  z-index: -1;\n\/*   border: 1px solid #f00; *\/\n}\n\n <\/pre><\/div>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;cobalt&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:false,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">\/\/ load webfont from Google Fonts\nWebFont.load({\n  google: {\n    families: ['Muli:600'] },\n\n  classes: false,\n  active: () =&gt; {\n    createCanvas();\n    updateCanvas();\n  } });\n\n\n\/\/ vertex shader\nconst vertexShader = `\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float time;\nuniform float index;\nuniform float divisions;\nuniform float tOffsetX;\nuniform vec4 tween;\nattribute vec3 position;\nattribute vec2 uv;\nvarying vec2 vUv;\n\nmat2 scale2D(vec2 scale) {\nreturn mat2(scale.x, 0.0, 0.0, scale.y);\n}\n\nvoid main() {\nvUv = uv;\nvec3 pos = position;\n\nfloat scaleX = tween.x;\npos.x += tOffsetX;\npos.xy *= scale2D(vec2(scaleX, 1.0));\npos.x -= tOffsetX;\n\ngl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);\n}\n`;\n\n\/\/ fragment shader\nconst fragmentShader = `\nprecision highp float;\n\nuniform sampler2D texture;\nuniform float time;\nuniform float index;\nuniform float divisions;\nuniform vec4 tween;\nvarying vec2 vUv;\n\nvoid main() {\nvec2 uv = vUv;\nfloat startV = index * (1.0 \/ divisions);\nvec2 tuv = vec2(uv.x, startV + uv.y * (1.0 \/ divisions));\n\nvec4 color = texture2D(texture, tuv);\n\ngl_FragColor = color;\n\/\/gl_FragColor = vec4(vec3(tuv.y), 1.0);\n}\n`;\n\n\/\/ general settings\nconst width = 960;\nconst height = 540;\nconst aspectRatio = width \/ height;\nconst dpr = Math.min(window.devicePixelRatio, 2);\nconst sceneWidth = 2;\nconst sceneHeight = sceneWidth \/ aspectRatio;\nlet scene, camera, renderer;\n\n\/\/ title settings\nconst titleWidth = 2.0;\nconst titleHeight = 0.5;\nconst numDivisions = 40;\nconst titleMeshes = [];\nlet numMeshes = 0;\n\n\/\/ animation settings\nconst tweens1 = []; \/\/ array of tweens for title1\nconst tweens2 = []; \/\/ array of tweens for title2\nconst tweens3 = []; \/\/ array of tweens for title3\n\nfunction createCanvas() {\n  scene = new THREE.Scene();\n\n  camera = new THREE.OrthographicCamera(\n  -sceneWidth \/ 2,\n  sceneWidth \/ 2,\n  sceneHeight \/ 2,\n  -sceneHeight \/ 2,\n  0.01,\n  1000);\n\n  camera.position.set(0, 0, 1);\n\n  renderer = new THREE.WebGLRenderer({\n    antialias: true,\n    alpha: true,\n    canvas: document.getElementById('Canvas') });\n\n  renderer.setSize(width, height);\n  renderer.setPixelRatio(dpr);\n\n  const texture1 = createFontTexture({\n    width: 1024,\n    height: 256,\n    fontSize: 230,\n    text: 'EFFECT' });\n\n\n  const texture2 = createFontTexture({\n    width: 1024,\n    height: 256,\n    fontSize: 230,\n    text: 'TEXT' });\n\n\n  const texture3 = createFontTexture({\n    width: 1224,\n    height: 256,\n    fontSize: 230,\n    text: 'PROFIDEV' });\n\n\n  createTitle({\n    texture: texture1,\n    tweens: tweens1 });\n\n\n  createTitle({\n    texture: texture2,\n    tweens: tweens2 });\n\n\n  createTitle({\n    texture: texture3,\n    tweens: tweens3 });\n\n\n  numMeshes = titleMeshes.length;\n\n  animateTitle();\n}\n\nfunction createFontTexture(options) {\n  const canvas = document.createElement('canvas');\n  const width = options.width * dpr;\n  const height = options.height * dpr;\n  canvas.width = width;\n  canvas.height = height;\n\n  const ctx = canvas.getContext('2d');\n\n  \/\/ ctx.fillStyle = 'rgba(255, 0, 0, 0.1)'\n  \/\/ ctx.fillRect(0, 0, width, height)\n\n  ctx.font = `600 ${options.fontSize * dpr}px 'Muli'`;\n  ctx.textAlign = 'center';\n  ctx.textBaseline = 'middle';\n  ctx.fillStyle = '#fff';\n  ctx.fillText(options.text, width \/ 2, height \/ 2 + 25);\n\n  const texture = new THREE.CanvasTexture(canvas);\n  texture.minFilter = THREE.LinearFilter;\n  texture.magFilter = THREE.LinearFilter;\n  texture.format = THREE.RGBAFormat;\n  texture.needsUpdate = false;\n\n  return texture;\n}\n\nfunction createTitle(params) {\n  for (let i = 0; i &lt; numDivisions; i++) {\n    const dividedHeight = titleHeight \/ numDivisions;\n    const geometry = new THREE.PlaneBufferGeometry(titleWidth, dividedHeight);\n    geometry.removeAttribute('normal');\n\n    const material = new THREE.RawShaderMaterial({\n      uniforms: {\n        texture: { value: params.texture },\n        time: { value: 0 },\n        index: { value: i },\n        divisions: { value: numDivisions },\n        tOffsetX: { value: -1.0 },\n        tween: { value: new THREE.Vector4(0, 0, 1, 1) } },\n\n      vertexShader,\n      fragmentShader,\n      transparent: true });\n\n\n    const y = -titleHeight * 0.5 + i * dividedHeight + dividedHeight * 0.5;\n\n    const mesh = new THREE.Mesh(geometry, material);\n    mesh.position.y = y;\n\n    scene.add(mesh);\n    titleMeshes.push(mesh);\n    params.tweens.push(material.uniforms.tween.value);\n  }\n}\n\nfunction animateTitle() {\n  const tl = gsap.timeline({\n    repeat: -1,\n    defaults: {\n      ease: &quot;power3.inOut&quot;,\n      duration: 0.8,\n      stagger: 0.01 } });\n\n\n\n  const setTransformOffset = (titleIndex, value) =&gt; {\n    const startIndex = titleIndex * numDivisions;\n    const endIndex = startIndex + numDivisions;\n    for (let i = startIndex; i &lt; endIndex; i++) {\n      titleMeshes[i].material.uniforms.tOffsetX.value = value;\n    }\n  };\n\n  tl.to(tweens1, {\n    x: 1.0,\n    onStart: () =&gt; {\n      setTransformOffset(0, -1.0);\n    } },\n  'start');\n  tl.to(tweens1, {\n    x: 0.0,\n    onStart: () =&gt; {\n      setTransformOffset(0, 1.0);\n    } },\n  'next1');\n\n  tl.to(tweens2, {\n    x: 1.0,\n    onStart: () =&gt; {\n      setTransformOffset(1, -1.0);\n    } },\n  'next1');\n  tl.to(tweens2, {\n    x: 0.0,\n    onStart: () =&gt; {\n      setTransformOffset(1, 1.0);\n    } },\n  'next2');\n\n  tl.to(tweens3, {\n    x: 1.0,\n    onStart: () =&gt; {\n      setTransformOffset(2, -1.0);\n    } },\n  'next2');\n  tl.to(tweens3, {\n    x: 0.0,\n    onStart: () =&gt; {\n      setTransformOffset(2, 1.0);\n    } });\n\n}\n\nfunction updateCanvas() {\n  requestAnimationFrame(updateCanvas);\n\n  const time = performance.now() * 0.001;\n  for (let i = 0; i &lt; numMeshes; i++) {\n    const mesh = titleMeshes[i];\n    mesh.material.uniforms.time.value = time;\n  }\n\n  renderer.render(scene, camera);\n}<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":1209,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19],"tags":[27,40],"class_list":["post-1208","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-elements","tag-gsap","tag-text"],"_links":{"self":[{"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/posts\/1208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/comments?post=1208"}],"version-history":[{"count":1,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/posts\/1208\/revisions"}],"predecessor-version":[{"id":1210,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/posts\/1208\/revisions\/1210"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/media\/1209"}],"wp:attachment":[{"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/media?parent=1208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/categories?post=1208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/notes.profidev.online\/ru\/wp-json\/wp\/v2\/tags?post=1208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}