Canvas.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import React from 'react'
  21. import { DEFAULT_SECONDARY_COLOR } from 'app/globalConstants'
  22. const styles = require('./Background.less')
  23. import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera'
  24. import { Scene } from 'three/src/scenes/Scene'
  25. import { BufferAttribute } from 'three/src/core/BufferAttribute'
  26. import { BufferGeometry } from 'three/src/core/BufferGeometry'
  27. import { Color } from 'three/src/math/Color'
  28. import { Points } from 'three/src/objects/Points'
  29. import { ShaderMaterial } from 'three/src/materials/ShaderMaterial'
  30. import { WebGLRenderer } from 'three/src/renderers/WebGLRenderer'
  31. export class Canvas extends React.Component<{}, {}> {
  32. constructor (props) {
  33. super(props)
  34. }
  35. private removeListeners: () => any = null
  36. private container = React.createRef<HTMLDivElement>()
  37. public componentDidMount () {
  38. // TODO to verify the performance under one or multiple chunks
  39. this.drawBackground()
  40. }
  41. public componentWillUnmount () {
  42. if (this.removeListeners) {
  43. this.removeListeners()
  44. }
  45. }
  46. private drawBackground = () => {
  47. const SEPARATION = 100
  48. const AMOUNTX = 50
  49. const AMOUNTY = 50
  50. let camera
  51. let scene
  52. let renderer
  53. let particles
  54. let count = 0
  55. let mouseX = 0
  56. let mouseY = 0
  57. let windowHalfX = window.innerWidth / 2
  58. let windowHalfY = window.innerHeight / 2
  59. const init = () => {
  60. const container = this.container.current
  61. camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000)
  62. camera.position.z = 1000
  63. scene = new Scene()
  64. const numParticles = AMOUNTX * AMOUNTY
  65. const positions = new Float32Array(numParticles * 3)
  66. const scales = new Float32Array(numParticles)
  67. let i = 0
  68. let j = 0
  69. for (let ix = 0; ix < AMOUNTX; ix++) {
  70. for (let iy = 0; iy < AMOUNTY; iy++) {
  71. positions[i] = ix * SEPARATION - ((AMOUNTX * SEPARATION) / 2) // x
  72. positions[i + 1] = 0 // y
  73. positions[i + 2] = iy * SEPARATION - ((AMOUNTY * SEPARATION) / 2) // z
  74. scales[j] = 1
  75. i += 3
  76. j++
  77. }
  78. }
  79. const geometry = new BufferGeometry()
  80. geometry.addAttribute('position', new BufferAttribute(positions, 3))
  81. geometry.addAttribute('scale', new BufferAttribute(scales, 1))
  82. const material = new ShaderMaterial({
  83. uniforms: {
  84. color: {
  85. value: new Color(0xffffff)
  86. }
  87. },
  88. vertexShader: document.getElementById('vertexshader').textContent,
  89. fragmentShader: document.getElementById('fragmentshader').textContent
  90. })
  91. //
  92. particles = new Points(geometry, material)
  93. scene.add(particles)
  94. renderer = new WebGLRenderer({ antialias: true })
  95. renderer.setPixelRatio(window.devicePixelRatio || 1)
  96. renderer.setSize(window.innerWidth, window.innerHeight)
  97. renderer.setClearColor(parseInt(DEFAULT_SECONDARY_COLOR.substr(1), 16))
  98. container.appendChild(renderer.domElement)
  99. document.addEventListener('mousemove', onDocumentMouseMove, false)
  100. container.addEventListener('touchstart', onDocumentTouchStart, false)
  101. container.addEventListener('touchmove', onDocumentTouchMove, false)
  102. window.addEventListener('resize', onWindowResize, false)
  103. this.removeListeners = () => {
  104. document.removeEventListener('mousemove', onDocumentMouseMove, false)
  105. container.removeEventListener('touchstart', onDocumentTouchStart, false)
  106. container.removeEventListener('touchmove', onDocumentTouchMove, false)
  107. window.removeEventListener('resize', onWindowResize, false)
  108. }
  109. }
  110. function onWindowResize () {
  111. windowHalfX = window.innerWidth / 2
  112. windowHalfY = window.innerHeight / 2
  113. camera.aspect = window.innerWidth / window.innerHeight
  114. camera.updateProjectionMatrix()
  115. renderer.setSize(window.innerWidth, window.innerHeight)
  116. }
  117. function onDocumentMouseMove (event) {
  118. mouseX = event.clientX - windowHalfX
  119. mouseY = event.clientY - windowHalfY
  120. }
  121. function onDocumentTouchStart (event) {
  122. if (event.touches.length === 1) {
  123. event.preventDefault()
  124. mouseX = event.touches[0].pageX - windowHalfX
  125. mouseY = event.touches[0].pageY - windowHalfY
  126. }
  127. }
  128. function onDocumentTouchMove (event) {
  129. if (event.touches.length === 1) {
  130. event.preventDefault()
  131. mouseX = event.touches[0].pageX - windowHalfX
  132. mouseY = event.touches[0].pageY - windowHalfY
  133. }
  134. }
  135. function animate () {
  136. requestAnimationFrame(animate)
  137. render()
  138. }
  139. function render () {
  140. camera.position.x += (mouseX - camera.position.x) * .05
  141. camera.position.y += (-mouseY - camera.position.y) * .05
  142. camera.lookAt(scene.position)
  143. const positions = particles.geometry.attributes.position.array
  144. const scales = particles.geometry.attributes.scale.array
  145. let i = 0
  146. let j = 0
  147. for (let ix = 0; ix < AMOUNTX; ix++) {
  148. for (let iy = 0; iy < AMOUNTY; iy++) {
  149. positions[i + 1] = (Math.sin((ix + count) * 0.3) * 50) +
  150. (Math.sin((iy + count) * 0.5) * 50)
  151. scales[j] = (Math.sin((ix + count) * 0.3) + 1) * 8 +
  152. (Math.sin((iy + count) * 0.5) + 1) * 8
  153. i += 3
  154. j++
  155. }
  156. }
  157. particles.geometry.attributes.position.needsUpdate = true
  158. particles.geometry.attributes.scale.needsUpdate = true
  159. renderer.render(scene, camera)
  160. count += 0.1
  161. }
  162. init()
  163. animate()
  164. }
  165. public render () {
  166. return (
  167. <div ref={this.container} className={styles.background}/>
  168. )
  169. }
  170. }
  171. export default Canvas