├── README.md ├── demo1.html ├── demo2.html └── js ├── FSS.js ├── jquery.js └── vector.js /README.md: -------------------------------------------------------------------------------- 1 | vector 2 | ====== 3 | 4 | 基于Canvas实现的炫酷3D动画大背景 5 | 6 | ## 前言 7 | 8 | HTML5 中新增标签Canvas,Canvas 对象表示一个 HTML 画布元素 -。它没有自己的行为,但是定义了一个 API 支持脚本化客户端绘图操作。利用JS,可以实现一些超炫酷的效果。本文所介绍的是基于Canvas实现的炫酷3D动画大背景。 9 | 10 | ## 案例一 11 | 12 | ### 原版 13 | 14 | QQ 官网 [http://im.qq.com/pcqq/](http://im.qq.com/pcqq/) 15 | 16 | ### demo 17 | 18 | [http://xuanfengge.com/demo/201411/vector/demo1.html](http://xuanfengge.com/demo/201411/vector/demo1.html) 19 | 20 | ### 使用 21 | 22 | 为方便大家使用,轩枫阁已将主体代码抽出,并示范使用方法(具体看源码),只需加载相关JS并调用即可。不依赖jQery,但是需注意代码执行顺序。 23 | 24 | ### 特点 25 | 26 | 改变3D大背景块颜色,清晰可见,适用于文字较少、加以配图的页面。 27 | 28 | [![sdfdsf](http://www.xuanfengge.com/wp-content/uploads/2014/11/sdfdsf.jpg)](http://www.xuanfengge.com/wp-content/uploads/2014/11/sdfdsf.jpg) 29 | 30 | ## 案例二 31 | 32 | ### 应用 33 | 34 | [http://www.xuanfengge.com/nav](http://www.xuanfengge.com/nav) 35 | 36 | ### demo 37 | 38 | [http://xuanfengge.com/demo/201411/vector/demo2.html](http://xuanfengge.com/demo/201411/vector/demo2.html) 39 | 40 | ### 特点 41 | 42 | 相比于demo1,这个方案主要是通过改变body的背景色来变换主题,配合CSS3渐变动画使得衔接自然。3D背景透明度降低,起衬托作用,适用于文字较多的页面。 43 | 44 | [![sdfgfdg](http://www.xuanfengge.com/wp-content/uploads/2014/11/sdfgfdg.jpg)](http://www.xuanfengge.com/wp-content/uploads/2014/11/sdfgfdg.jpg) -------------------------------------------------------------------------------- /demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 轩枫阁-基于Canvas实现的炫酷3D动画大背景 6 | 67 | 68 | 69 | 87 | 88 | 89 |
90 | 91 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /demo2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 轩枫阁-基于Canvas实现的炫酷3D动画大背景 6 | 90 | 91 | 92 | 100 | 101 | 102 | 103 |
104 |
105 |
106 | 107 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /js/FSS.js: -------------------------------------------------------------------------------- 1 | function FSS(container, output){ 2 | FSS = { 3 | FRONT: 0, 4 | BACK: 1, 5 | DOUBLE: 2, 6 | SVGNS: "http://www.w3.org/2000/svg" 7 | }; 8 | FSS.Array = "function" === typeof Float32Array ? Float32Array: Array; 9 | FSS.Utils = { 10 | isNumber: function(b) { 11 | return ! isNaN(parseFloat(b)) && isFinite(b) 12 | } 13 | }; 14 | 15 | 16 | (function() { 17 | for (var b = 0, d = ["ms", "moz", "webkit", "o"], h = 0; h < d.length && !window.requestAnimationFrame; ++h) window.requestAnimationFrame = window[d[h] + "RequestAnimationFrame"], 18 | window.cancelAnimationFrame = window[d[h] + "CancelAnimationFrame"] || window[d[h] + "CancelRequestAnimationFrame"]; 19 | window.requestAnimationFrame || (window.requestAnimationFrame = function(d, h) { 20 | var n = (new Date).getTime(), 21 | s = Math.max(0, 16 - (n - b)), 22 | k = window.setTimeout(function() { 23 | d(n + s) 24 | }, 25 | s); 26 | b = n + s; 27 | return k 28 | }); 29 | window.cancelAnimationFrame || (window.cancelAnimationFrame = function(b) { 30 | clearTimeout(b) 31 | }) 32 | })(); 33 | Math.PIM2 = 2 * Math.PI; 34 | Math.PID2 = Math.PI / 2; 35 | Math.randomInRange = function(b, d) { 36 | return b + (d - b) * Math.random() 37 | }; 38 | Math.clamp = function(b, d, h) { 39 | b = Math.max(b, d); 40 | return b = Math.min(b, h) 41 | }; 42 | FSS.Vector3 = { 43 | create: function(b, d, h) { 44 | var g = new FSS.Array(3); 45 | this.set(g, b, d, h); 46 | return g 47 | }, 48 | clone: function(b) { 49 | var d = this.create(); 50 | this.copy(d, b); 51 | return d 52 | }, 53 | set: function(b, d, h, g) { 54 | b[0] = d || 0; 55 | b[1] = h || 0; 56 | b[2] = g || 0; 57 | return this 58 | }, 59 | setX: function(b, d) { 60 | b[0] = d || 0; 61 | return this 62 | }, 63 | setY: function(b, d) { 64 | b[1] = d || 0; 65 | return this 66 | }, 67 | setZ: function(b, d) { 68 | b[2] = d || 0; 69 | return this 70 | }, 71 | copy: function(b, d) { 72 | b[0] = d[0]; 73 | b[1] = d[1]; 74 | b[2] = d[2]; 75 | return this 76 | }, 77 | add: function(b, d) { 78 | b[0] += d[0]; 79 | b[1] += d[1]; 80 | b[2] += d[2]; 81 | return this 82 | }, 83 | addVectors: function(b, d, h) { 84 | b[0] = d[0] + 85 | h[0]; 86 | b[1] = d[1] + h[1]; 87 | b[2] = d[2] + h[2]; 88 | return this 89 | }, 90 | addScalar: function(b, d) { 91 | b[0] += d; 92 | b[1] += d; 93 | b[2] += d; 94 | return this 95 | }, 96 | subtract: function(b, d) { 97 | b[0] -= d[0]; 98 | b[1] -= d[1]; 99 | b[2] -= d[2]; 100 | return this 101 | }, 102 | subtractVectors: function(b, d, h) { 103 | b[0] = d[0] - h[0]; 104 | b[1] = d[1] - h[1]; 105 | b[2] = d[2] - h[2]; 106 | return this 107 | }, 108 | subtractScalar: function(b, d) { 109 | b[0] -= d; 110 | b[1] -= d; 111 | b[2] -= d; 112 | return this 113 | }, 114 | multiply: function(b, d) { 115 | b[0] *= d[0]; 116 | b[1] *= d[1]; 117 | b[2] *= d[2]; 118 | return this 119 | }, 120 | multiplyVectors: function(b, d, h) { 121 | b[0] = d[0] * h[0]; 122 | b[1] = d[1] * h[1]; 123 | b[2] = d[2] * h[2]; 124 | return this 125 | }, 126 | multiplyScalar: function(b, d) { 127 | b[0] *= d; 128 | b[1] *= d; 129 | b[2] *= d; 130 | return this 131 | }, 132 | divide: function(b, d) { 133 | b[0] /= d[0]; 134 | b[1] /= d[1]; 135 | b[2] /= d[2]; 136 | return this 137 | }, 138 | divideVectors: function(b, d, h) { 139 | b[0] = d[0] / h[0]; 140 | b[1] = d[1] / h[1]; 141 | b[2] = d[2] / h[2]; 142 | return this 143 | }, 144 | divideScalar: function(b, d) { 145 | 0 !== d ? (b[0] /= d, b[1] /= d, b[2] /= d) : (b[0] = 0, b[1] = 0, b[2] = 0); 146 | return this 147 | }, 148 | cross: function(b, d) { 149 | var h = b[0], 150 | g = b[1], 151 | l = b[2]; 152 | b[0] = g * d[2] - l * d[1]; 153 | b[1] = l * d[0] - h * d[2]; 154 | b[2] = h * d[1] - g * d[0]; 155 | return this 156 | }, 157 | crossVectors: function(b, d, h) { 158 | b[0] = d[1] * h[2] - d[2] * h[1]; 159 | b[1] = d[2] * h[0] - d[0] * h[2]; 160 | b[2] = d[0] * h[1] - d[1] * h[0]; 161 | return this 162 | }, 163 | min: function(b, d) { 164 | b[0] < d && (b[0] = d); 165 | b[1] < d && (b[1] = d); 166 | b[2] < d && (b[2] = d); 167 | return this 168 | }, 169 | max: function(b, d) { 170 | b[0] > d && (b[0] = d); 171 | b[1] > d && (b[1] = d); 172 | b[2] > d && (b[2] = d); 173 | return this 174 | }, 175 | clamp: function(b, d, h) { 176 | this.min(b, d); 177 | this.max(b, h); 178 | return this 179 | }, 180 | limit: function(b, d, h) { 181 | var g = this.length(b); 182 | null !== d && g < d ? this.setLength(b, d) : null !== h && g > h && this.setLength(b, h); 183 | return this 184 | }, 185 | dot: function(b, d) { 186 | return b[0] * d[0] + b[1] * d[1] + b[2] * d[2] 187 | }, 188 | normalise: function(b) { 189 | return this.divideScalar(b, this.length(b)) 190 | }, 191 | negate: function(b) { 192 | return this.multiplyScalar(b, -1) 193 | }, 194 | distanceSquared: function(b, d) { 195 | var h = b[0] - d[0], 196 | g = b[1] - d[1], 197 | l = b[2] - d[2]; 198 | return h * h + g * g + l * l 199 | }, 200 | distance: function(b, d) { 201 | return Math.sqrt(this.distanceSquared(b, d)) 202 | }, 203 | lengthSquared: function(b) { 204 | return b[0] * b[0] + b[1] * b[1] + b[2] * b[2] 205 | }, 206 | length: function(b) { 207 | return Math.sqrt(this.lengthSquared(b)) 208 | }, 209 | setLength: function(b, d) { 210 | var h = this.length(b); 211 | 0 !== h && d !== h && this.multiplyScalar(b, d / h); 212 | return this 213 | } 214 | }; 215 | FSS.Vector4 = { 216 | create: function(b, d, h, g) { 217 | g = new FSS.Array(4); 218 | this.set(g, b, d, h); 219 | return g 220 | }, 221 | set: function(b, d, h, g, l) { 222 | b[0] = d || 0; 223 | b[1] = h || 0; 224 | b[2] = g || 0; 225 | b[3] = l || 0; 226 | return this 227 | }, 228 | setX: function(b, d) { 229 | b[0] = d || 0; 230 | return this 231 | }, 232 | setY: function(b, d) { 233 | b[1] = d || 0; 234 | return this 235 | }, 236 | setZ: function(b, d) { 237 | b[2] = d || 0; 238 | return this 239 | }, 240 | setW: function(b, d) { 241 | b[3] = d || 0; 242 | return this 243 | }, 244 | add: function(b, d) { 245 | b[0] += d[0]; 246 | b[1] += d[1]; 247 | b[2] += d[2]; 248 | b[3] += d[3]; 249 | return this 250 | }, 251 | multiplyVectors: function(b, d, h) { 252 | b[0] = d[0] * h[0]; 253 | b[1] = d[1] * h[1]; 254 | b[2] = d[2] * h[2]; 255 | b[3] = d[3] * h[3]; 256 | return this 257 | }, 258 | multiplyScalar: function(b, d) { 259 | b[0] *= d; 260 | b[1] *= d; 261 | b[2] *= d; 262 | b[3] *= d; 263 | return this 264 | }, 265 | min: function(b, d) { 266 | b[0] < d && (b[0] = d); 267 | b[1] < d && (b[1] = d); 268 | b[2] < d && (b[2] = d); 269 | b[3] < d && (b[3] = d); 270 | return this 271 | }, 272 | max: function(b, d) { 273 | b[0] > d && (b[0] = d); 274 | b[1] > d && (b[1] = d); 275 | b[2] > d && (b[2] = d); 276 | b[3] > d && (b[3] = d); 277 | return this 278 | }, 279 | clamp: function(b, d, h) { 280 | this.min(b, d); 281 | this.max(b, h); 282 | return this 283 | } 284 | }; 285 | FSS.Color = function(b, d) { 286 | this.rgba = FSS.Vector4.create(); 287 | this.hex = b || "#000000"; 288 | this.opacity = FSS.Utils.isNumber(d) ? d: 1; 289 | this.set(this.hex, this.opacity) 290 | }; 291 | FSS.Color.prototype = { 292 | set: function(b, d) { 293 | b = b.replace("#", ""); 294 | var h = b.length / 3; 295 | this.rgba[0] = parseInt(b.substring(0 * h, 1 * h), 16) / 255; 296 | this.rgba[1] = parseInt(b.substring(1 * h, 2 * h), 16) / 255; 297 | this.rgba[2] = parseInt(b.substring(2 * h, 3 * h), 16) / 255; 298 | this.rgba[3] = FSS.Utils.isNumber(d) ? d: this.rgba[3]; 299 | return this 300 | }, 301 | hexify: function(b) { 302 | b = Math.ceil(255 * b).toString(16); 303 | 1 === b.length && (b = "0" + b); 304 | return b 305 | }, 306 | format: function() { 307 | var b = this.hexify(this.rgba[0]), 308 | d = this.hexify(this.rgba[1]), 309 | h = this.hexify(this.rgba[2]); 310 | return this.hex = "#" + 311 | b + d + h 312 | } 313 | }; 314 | FSS.Object = function() { 315 | this.position = FSS.Vector3.create() 316 | }; 317 | FSS.Object.prototype = { 318 | setPosition: function(b, d, h) { 319 | FSS.Vector3.set(this.position, b, d, h); 320 | return this 321 | } 322 | }; 323 | FSS.Light = function(b, d) { 324 | FSS.Object.call(this); 325 | this.ambient = new FSS.Color(b || "#FFFFFF"); 326 | this.diffuse = new FSS.Color(d || "#FFFFFF"); 327 | this.ray = FSS.Vector3.create() 328 | }; 329 | FSS.Light.prototype = Object.create(FSS.Object.prototype); 330 | FSS.Vertex = function(b, d, h) { 331 | this.position = FSS.Vector3.create(b, d, h) 332 | }; 333 | FSS.Vertex.prototype = { 334 | setPosition: function(b, d, h) { 335 | FSS.Vector3.set(this.position, b, d, h); 336 | return this 337 | } 338 | }; 339 | FSS.Triangle = function(b, d, h) { 340 | this.a = b || new FSS.Vertex; 341 | this.b = d || new FSS.Vertex; 342 | this.c = h || new FSS.Vertex; 343 | this.vertices = [this.a, this.b, this.c]; 344 | this.u = FSS.Vector3.create(); 345 | this.v = FSS.Vector3.create(); 346 | this.centroid = FSS.Vector3.create(); 347 | this.normal = FSS.Vector3.create(); 348 | this.color = new FSS.Color; 349 | this.polygon = document.createElementNS(FSS.SVGNS, "polygon"); 350 | this.polygon.setAttributeNS(null, "stroke-linejoin", "round"); 351 | this.polygon.setAttributeNS(null, "stroke-miterlimit", "1"); 352 | this.polygon.setAttributeNS(null, "stroke-width", "1"); 353 | this.computeCentroid(); 354 | this.computeNormal() 355 | }; 356 | FSS.Triangle.prototype = { 357 | computeCentroid: function() { 358 | this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; 359 | this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; 360 | this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; 361 | FSS.Vector3.divideScalar(this.centroid, 3); 362 | return this 363 | }, 364 | computeNormal: function() { 365 | FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); 366 | FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); 367 | FSS.Vector3.crossVectors(this.normal, this.u, this.v); 368 | FSS.Vector3.normalise(this.normal); 369 | return this 370 | } 371 | }; 372 | FSS.Geometry = function() { 373 | this.vertices = []; 374 | this.triangles = []; 375 | this.dirty = !1 376 | }; 377 | FSS.Geometry.prototype = { 378 | update: function() { 379 | if (this.dirty) { 380 | var b, 381 | d; 382 | for (b = this.triangles.length - 1; 0 <= b; b--) d = this.triangles[b], 383 | d.computeCentroid(), 384 | d.computeNormal(); 385 | this.dirty = !1 386 | } 387 | return this 388 | } 389 | }; 390 | FSS.Plane = function(b, d, h, g) { 391 | FSS.Geometry.call(this); 392 | this.width = b || 100; 393 | this.height = d || 100; 394 | this.segments = h || 4; 395 | this.slices = g || 4; 396 | this.segmentWidth = this.width / this.segments; 397 | this.sliceHeight = this.height / this.slices; 398 | var l, 399 | n, 400 | s; 401 | h = []; 402 | l = -0.5 * this.width; 403 | n = 0.5 * this.height; 404 | for (b = 0; b <= this.segments; b++) for (h.push([]), d = 0; d <= this.slices; d++) g = new FSS.Vertex(l + b * this.segmentWidth, n - d * this.sliceHeight), 405 | h[b].push(g), 406 | this.vertices.push(g); 407 | for (b = 0; b < this.segments; b++) for (d = 0; d < this.slices; d++) g = h[b + 0][d + 0], 408 | l = h[b + 0][d + 409 | 1], 410 | n = h[b + 1][d + 0], 411 | s = h[b + 1][d + 1], 412 | t0 = new FSS.Triangle(g, l, n), 413 | t1 = new FSS.Triangle(n, l, s), 414 | this.triangles.push(t0, t1) 415 | }; 416 | FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); 417 | FSS.Material = function(b, d) { 418 | this.ambient = new FSS.Color(b || "#444444"); 419 | this.diffuse = new FSS.Color(d || "#FFFFFF"); 420 | this.slave = new FSS.Color 421 | }; 422 | FSS.Mesh = function(b, d) { 423 | FSS.Object.call(this); 424 | this.geometry = b || new FSS.Geometry; 425 | this.material = d || new FSS.Material; 426 | this.side = FSS.FRONT; 427 | this.visible = !0 428 | }; 429 | FSS.Mesh.prototype = Object.create(FSS.Object.prototype); 430 | FSS.Mesh.prototype.update = function(b, d) { 431 | var h, 432 | g, 433 | l, 434 | n, 435 | s; 436 | this.geometry.update(); 437 | if (d) for (h = this.geometry.triangles.length - 1; 0 <= h; h--) { 438 | g = this.geometry.triangles[h]; 439 | FSS.Vector4.set(g.color.rgba); 440 | for (l = b.length - 1; 0 <= l; l--) n = b[l], 441 | FSS.Vector3.subtractVectors(n.ray, n.position, g.centroid), 442 | FSS.Vector3.normalise(n.ray), 443 | s = FSS.Vector3.dot(g.normal, n.ray), 444 | this.side === FSS.FRONT ? s = Math.max(s, 0) : this.side === FSS.BACK ? s = Math.abs(Math.min(s, 0)) : this.side === FSS.DOUBLE && (s = Math.max(Math.abs(s), 0)), 445 | FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.ambient.rgba, n.ambient.rgba), 446 | FSS.Vector4.add(g.color.rgba, this.material.slave.rgba), 447 | FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.diffuse.rgba, n.diffuse.rgba), 448 | FSS.Vector4.multiplyScalar(this.material.slave.rgba, s), 449 | FSS.Vector4.add(g.color.rgba, this.material.slave.rgba); 450 | FSS.Vector4.clamp(g.color.rgba, 0, 1) 451 | } 452 | return this 453 | }; 454 | FSS.Scene = function() { 455 | this.meshes = []; 456 | this.lights = [] 457 | }; 458 | FSS.Scene.prototype = { 459 | add: function(b) { 460 | b instanceof FSS.Mesh && !~this.meshes.indexOf(b) ? this.meshes.push(b) : b instanceof FSS.Light && !~this.lights.indexOf(b) && this.lights.push(b); 461 | return this 462 | }, 463 | remove: function(b) { 464 | b instanceof FSS.Mesh && ~this.meshes.indexOf(b) ? this.meshes.splice(this.meshes.indexOf(b), 1) : b instanceof FSS.Light && ~this.lights.indexOf(b) && this.lights.splice(this.lights.indexOf(b), 1); 465 | return this 466 | } 467 | }; 468 | FSS.Renderer = function() { 469 | this.halfHeight = this.halfWidth = this.height = this.width = 0 470 | }; 471 | FSS.Renderer.prototype = { 472 | setSize: function(b, d) { 473 | if (this.width !== b || this.height !== d) return this.width = b, 474 | this.height = d, 475 | this.halfWidth = 0.5 * this.width, 476 | this.halfHeight = 0.5 * this.height, 477 | this 478 | }, 479 | clear: function() { 480 | return this 481 | }, 482 | render: function(b) { 483 | return this 484 | } 485 | }; 486 | FSS.CanvasRenderer = function() { 487 | FSS.Renderer.call(this); 488 | this.element = document.createElement("canvas"); 489 | this.element.style.display = "block"; 490 | this.context = this.element.getContext("2d"); 491 | this.setSize(this.element.width, this.element.height) 492 | }; 493 | FSS.CanvasRenderer.prototype = Object.create(FSS.Renderer.prototype); 494 | FSS.CanvasRenderer.prototype.setSize = function(b, d) { 495 | FSS.Renderer.prototype.setSize.call(this, b, d); 496 | this.element.width = b; 497 | this.element.height = d; 498 | this.context.setTransform(1, 0, 0, -1, this.halfWidth, this.halfHeight); 499 | return this 500 | }; 501 | FSS.CanvasRenderer.prototype.clear = function() { 502 | FSS.Renderer.prototype.clear.call(this); 503 | this.context.clearRect( - this.halfWidth, -this.halfHeight, this.width, this.height); 504 | return this 505 | }; 506 | FSS.CanvasRenderer.prototype.render = function(b) { 507 | FSS.Renderer.prototype.render.call(this, b); 508 | var d, 509 | h, 510 | g, 511 | l, 512 | n; 513 | this.clear(); 514 | this.context.lineJoin = "round"; 515 | this.context.lineWidth = 1; 516 | for (d = b.meshes.length - 1; 0 <= d; d--) if (h = b.meshes[d], h.visible) for (h.update(b.lights, !0), g = h.geometry.triangles.length - 1; 0 <= g; g--) l = h.geometry.triangles[g], 517 | n = l.color.format(), 518 | this.context.beginPath(), 519 | this.context.moveTo(l.a.position[0], l.a.position[1]), 520 | this.context.lineTo(l.b.position[0], l.b.position[1]), 521 | this.context.lineTo(l.c.position[0], l.c.position[1]), 522 | this.context.closePath(), 523 | this.context.strokeStyle = n, 524 | this.context.fillStyle = n, 525 | this.context.stroke(), 526 | this.context.fill(); 527 | return this 528 | }; 529 | FSS.WebGLRenderer = function() { 530 | FSS.Renderer.call(this); 531 | this.element = document.createElement("canvas"); 532 | this.element.style.display = "block"; 533 | this.lights = this.vertices = null; 534 | this.gl = this.getContext(this.element, { 535 | preserveDrawingBuffer: !1, 536 | premultipliedAlpha: !0, 537 | antialias: !0, 538 | stencil: !0, 539 | alpha: !0 540 | }); 541 | if (this.unsupported = !this.gl) return "WebGL is not supported by your browser."; 542 | this.gl.clearColor(0, 0, 0, 0); 543 | this.gl.enable(this.gl.DEPTH_TEST); 544 | this.setSize(this.element.width, this.element.height) 545 | }; 546 | FSS.WebGLRenderer.prototype = Object.create(FSS.Renderer.prototype); 547 | FSS.WebGLRenderer.prototype.getContext = function(b, d) { 548 | var h = !1; 549 | try { 550 | if (! (h = b.getContext("experimental-webgl", d))) throw "Error creating WebGL context."; 551 | } catch(g) { 552 | // console.error(g); 553 | } 554 | return h 555 | }; 556 | FSS.WebGLRenderer.prototype.setSize = function(b, d) { 557 | FSS.Renderer.prototype.setSize.call(this, b, d); 558 | if (!this.unsupported) return this.element.width = b, 559 | this.element.height = d, 560 | this.gl.viewport(0, 0, b, d), 561 | this 562 | }; 563 | FSS.WebGLRenderer.prototype.clear = function() { 564 | FSS.Renderer.prototype.clear.call(this); 565 | if (!this.unsupported) return this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT), 566 | this 567 | }; 568 | FSS.WebGLRenderer.prototype.render = function(b) { 569 | FSS.Renderer.prototype.render.call(this, b); 570 | if (!this.unsupported) { 571 | var d, 572 | h, 573 | g, 574 | l, 575 | n, 576 | s, 577 | k, 578 | m; 579 | s = !1; 580 | var r = b.lights.length, 581 | u, 582 | E, 583 | y, 584 | z = 0; 585 | this.clear(); 586 | if (this.lights !== r) if (this.lights = r, 0 < this.lights) this.buildProgram(r); 587 | else return; 588 | if (this.program) { 589 | for (d = b.meshes.length - 1; 0 <= d; d--) h = b.meshes[d], 590 | h.geometry.dirty && (s = !0), 591 | h.update(b.lights, !1), 592 | z += 3 * h.geometry.triangles.length; 593 | if (s || this.vertices !== z) for (k in this.vertices = z, this.program.attributes) { 594 | s = this.program.attributes[k]; 595 | s.data = new FSS.Array(z * s.size); 596 | u = 0; 597 | for (d = b.meshes.length - 1; 0 <= d; d--) for (h = b.meshes[d], g = 0, l = h.geometry.triangles.length; g < l; g++) for (n = h.geometry.triangles[g], E = 0, y = n.vertices.length; E < y; E++) { 598 | vertex = n.vertices[E]; 599 | switch (k) { 600 | case "side": 601 | this.setBufferData(u, s, h.side); 602 | break; 603 | case "position": 604 | this.setBufferData(u, s, vertex.position); 605 | break; 606 | case "centroid": 607 | this.setBufferData(u, s, n.centroid); 608 | break; 609 | case "normal": 610 | this.setBufferData(u, s, n.normal); 611 | break; 612 | case "ambient": 613 | this.setBufferData(u, s, h.material.ambient.rgba); 614 | break; 615 | case "diffuse": 616 | this.setBufferData(u, s, h.material.diffuse.rgba) 617 | } 618 | u++ 619 | } 620 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, s.buffer); 621 | this.gl.bufferData(this.gl.ARRAY_BUFFER, s.data, this.gl.DYNAMIC_DRAW); 622 | this.gl.enableVertexAttribArray(s.location); 623 | this.gl.vertexAttribPointer(s.location, s.size, this.gl.FLOAT, !1, 0, 0) 624 | } 625 | this.setBufferData(0, this.program.uniforms.resolution, [this.width, this.height, this.width]); 626 | for (s = r - 1; 0 <= s; s--) d = b.lights[s], 627 | this.setBufferData(s, this.program.uniforms.lightPosition, d.position), 628 | this.setBufferData(s, this.program.uniforms.lightAmbient, d.ambient.rgba), 629 | this.setBufferData(s, this.program.uniforms.lightDiffuse, d.diffuse.rgba); 630 | for (m in this.program.uniforms) switch (s = this.program.uniforms[m], d = s.location, b = s.data, s.structure) { 631 | case "3f": 632 | this.gl.uniform3f(d, b[0], b[1], b[2]); 633 | break; 634 | case "3fv": 635 | this.gl.uniform3fv(d, b); 636 | break; 637 | case "4fv": 638 | this.gl.uniform4fv(d, b) 639 | } 640 | } 641 | this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertices); 642 | return this 643 | } 644 | }; 645 | FSS.WebGLRenderer.prototype.setBufferData = function(b, d, h) { 646 | if (FSS.Utils.isNumber(h)) d.data[b * d.size] = h; 647 | else for (var g = h.length - 1; 0 <= g; g--) d.data[b * d.size + g] = h[g] 648 | }; 649 | FSS.WebGLRenderer.prototype.buildProgram = function(b) { 650 | if (!this.unsupported) { 651 | var d = FSS.WebGLRenderer.VS(b), 652 | h = FSS.WebGLRenderer.FS(b), 653 | g = d + h; 654 | if (!this.program || this.program.code !== g) { 655 | var l = this.gl.createProgram(), 656 | d = this.buildShader(this.gl.VERTEX_SHADER, d), 657 | h = this.buildShader(this.gl.FRAGMENT_SHADER, h); 658 | this.gl.attachShader(l, d); 659 | this.gl.attachShader(l, h); 660 | this.gl.linkProgram(l); 661 | if (!this.gl.getProgramParameter(l, this.gl.LINK_STATUS)) return b = this.gl.getError(), 662 | l = this.gl.getProgramParameter(l, this.gl.VALIDATE_STATUS), 663 | console.error("Could not initialise shader.\nVALIDATE_STATUS: " + l + "\nERROR: " + b), 664 | null; 665 | this.gl.deleteShader(h); 666 | this.gl.deleteShader(d); 667 | l.code = g; 668 | l.attributes = { 669 | side: this.buildBuffer(l, "attribute", "aSide", 1, "f"), 670 | position: this.buildBuffer(l, "attribute", "aPosition", 3, "v3"), 671 | centroid: this.buildBuffer(l, "attribute", "aCentroid", 3, "v3"), 672 | normal: this.buildBuffer(l, "attribute", "aNormal", 3, "v3"), 673 | ambient: this.buildBuffer(l, "attribute", "aAmbient", 4, "v4"), 674 | diffuse: this.buildBuffer(l, "attribute", "aDiffuse", 4, "v4") 675 | }; 676 | l.uniforms = { 677 | resolution: this.buildBuffer(l, "uniform", "uResolution", 3, "3f", 1), 678 | lightPosition: this.buildBuffer(l, "uniform", "uLightPosition", 3, "3fv", b), 679 | lightAmbient: this.buildBuffer(l, "uniform", "uLightAmbient", 4, "4fv", b), 680 | lightDiffuse: this.buildBuffer(l, "uniform", "uLightDiffuse", 4, "4fv", b) 681 | }; 682 | this.program = l; 683 | this.gl.useProgram(this.program); 684 | return l 685 | } 686 | } 687 | }; 688 | FSS.WebGLRenderer.prototype.buildShader = function(b, d) { 689 | if (!this.unsupported) { 690 | var h = this.gl.createShader(b); 691 | this.gl.shaderSource(h, d); 692 | this.gl.compileShader(h); 693 | return this.gl.getShaderParameter(h, this.gl.COMPILE_STATUS) ? h: (console.error(this.gl.getShaderInfoLog(h)), null) 694 | } 695 | }; 696 | FSS.WebGLRenderer.prototype.buildBuffer = function(b, d, h, g, l, n) { 697 | l = { 698 | buffer: this.gl.createBuffer(), 699 | size: g, 700 | structure: l, 701 | data: null 702 | }; 703 | switch (d) { 704 | case "attribute": 705 | l.location = this.gl.getAttribLocation(b, h); 706 | break; 707 | case "uniform": 708 | l.location = this.gl.getUniformLocation(b, h) 709 | } 710 | n && (l.data = new FSS.Array(n * g)); 711 | return l 712 | }; 713 | FSS.WebGLRenderer.VS = function(b) { 714 | return ["precision mediump float;", "#define LIGHTS " + b, "attribute float aSide;\nattribute vec3 aPosition;\nattribute vec3 aCentroid;\nattribute vec3 aNormal;\nattribute vec4 aAmbient;\nattribute vec4 aDiffuse;\nuniform vec3 uResolution;\nuniform vec3 uLightPosition[LIGHTS];\nuniform vec4 uLightAmbient[LIGHTS];\nuniform vec4 uLightDiffuse[LIGHTS];\nvarying vec4 vColor;\nvoid main() {\nvColor = vec4(0.0);\nvec3 position = aPosition / uResolution * 2.0;\nfor (int i = 0; i < LIGHTS; i++) {\nvec3 lightPosition = uLightPosition[i];\nvec4 lightAmbient = uLightAmbient[i];\nvec4 lightDiffuse = uLightDiffuse[i];\nvec3 ray = normalize(lightPosition - aCentroid);\nfloat illuminance = dot(aNormal, ray);\nif (aSide == 0.0) {\nilluminance = max(illuminance, 0.0);\n} else if (aSide == 1.0) {\nilluminance = abs(min(illuminance, 0.0));\n} else if (aSide == 2.0) {\nilluminance = max(abs(illuminance), 0.0);\n}\nvColor += aAmbient * lightAmbient;\nvColor += aDiffuse * lightDiffuse * illuminance;\n}\nvColor = clamp(vColor, 0.0, 1.0);\ngl_Position = vec4(position, 1.0);\n}"].join("\n") 715 | }; 716 | FSS.WebGLRenderer.FS = function(b) { 717 | return "precision mediump float;\nvarying vec4 vColor;\nvoid main() {\ngl_FragColor = vColor;\n}" 718 | }; 719 | FSS.SVGRenderer = function() { 720 | FSS.Renderer.call(this); 721 | this.element = document.createElementNS(FSS.SVGNS, "svg"); 722 | this.element.setAttribute("xmlns", FSS.SVGNS); 723 | this.element.setAttribute("version", "1.1"); 724 | this.element.style.display = "block"; 725 | this.setSize(300, 150) 726 | }; 727 | FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); 728 | FSS.SVGRenderer.prototype.setSize = function(b, d) { 729 | FSS.Renderer.prototype.setSize.call(this, b, d); 730 | this.element.setAttribute("width", b); 731 | this.element.setAttribute("height", d); 732 | return this 733 | }; 734 | FSS.SVGRenderer.prototype.clear = function() { 735 | FSS.Renderer.prototype.clear.call(this); 736 | for (var b = this.element.childNodes.length - 1; 0 <= b; b--) this.element.removeChild(this.element.childNodes[b]); 737 | return this 738 | }; 739 | FSS.SVGRenderer.prototype.render = function(b) { 740 | FSS.Renderer.prototype.render.call(this, b); 741 | var d, 742 | h, 743 | g, 744 | l, 745 | n, 746 | s; 747 | for (d = b.meshes.length - 1; 0 <= d; d--) if (h = b.meshes[d], h.visible) for (h.update(b.lights, !0), g = h.geometry.triangles.length - 1; 0 <= g; g--) l = h.geometry.triangles[g], 748 | l.polygon.parentNode !== this.element && this.element.appendChild(l.polygon), 749 | n = this.formatPoint(l.a) + " ", 750 | n += this.formatPoint(l.b) + " ", 751 | n += this.formatPoint(l.c), 752 | s = this.formatStyle(l.color.format()), 753 | l.polygon.setAttributeNS(null, "points", n), 754 | l.polygon.setAttributeNS(null, "style", s); 755 | return this 756 | }; 757 | FSS.SVGRenderer.prototype.formatPoint = function(b) { 758 | return this.halfWidth + b.position[0] + "," + (this.halfHeight - b.position[1]) 759 | }; 760 | FSS.SVGRenderer.prototype.formatStyle = function(b) { 761 | return b = "fill:" + b + ";" + ("stroke:" + b + ";") 762 | }; 763 | 764 | /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || 765 | function() { 766 | function b() { 767 | B.remove(G); 768 | D.clear(); 769 | H = new FSS.Plane(l.width * D.width, l.height * D.height, l.segments, l.slices); 770 | J = new FSS.Material(l.ambient, l.diffuse); 771 | G = new FSS.Mesh(H, J); 772 | B.add(G); 773 | var b, 774 | d; 775 | for (b = H.vertices.length - 1; 0 <= b; b--) d = H.vertices[b], 776 | d.anchor = FSS.Vector3.clone(d.position), 777 | d.step = FSS.Vector3.create(Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1)), 778 | d.time = Math.randomInRange(0, Math.PIM2) 779 | } 780 | function d(d, g) { 781 | D.setSize(d, g); 782 | FSS.Vector3.set(E, D.halfWidth, D.halfHeight); 783 | b() 784 | } 785 | function h() { 786 | r = Date.now() - u; 787 | var b, 788 | d, 789 | k, 790 | m, 791 | s, 792 | z = l.depth / 2; 793 | FSS.Vector3.copy(n.bounds, E); 794 | FSS.Vector3.multiplyScalar(n.bounds, n.xyScalar); 795 | FSS.Vector3.setZ(y, n.zOffset); 796 | n.autopilot && (b = Math.sin(n.step[0] * r * n.speed), d = Math.cos(n.step[1] * r * n.speed), FSS.Vector3.set(y, n.bounds[0] * b, n.bounds[1] * d, n.zOffset)); 797 | for (b = B.lights.length - 1; 0 <= b; b--) d = B.lights[b], 798 | FSS.Vector3.setZ(d.position, n.zOffset), 799 | k = Math.clamp(FSS.Vector3.distanceSquared(d.position, y), n.minDistance, n.maxDistance), 800 | k = n.gravity * d.mass / k, 801 | FSS.Vector3.subtractVectors(d.force, y, d.position), 802 | FSS.Vector3.normalise(d.force), 803 | FSS.Vector3.multiplyScalar(d.force, k), 804 | FSS.Vector3.set(d.acceleration), 805 | FSS.Vector3.add(d.acceleration, d.force), 806 | FSS.Vector3.add(d.velocity, d.acceleration), 807 | FSS.Vector3.multiplyScalar(d.velocity, n.dampening), 808 | FSS.Vector3.limit(d.velocity, n.minLimit, n.maxLimit), 809 | FSS.Vector3.add(d.position, d.velocity); 810 | for (m = H.vertices.length - 1; 0 <= m; m--) s = H.vertices[m], 811 | b = Math.sin(s.time + s.step[0] * r * l.speed), 812 | d = Math.cos(s.time + s.step[1] * r * l.speed), 813 | k = Math.sin(s.time + s.step[2] * r * l.speed), 814 | FSS.Vector3.set(s.position, l.xRange * H.segmentWidth * b, l.yRange * H.sliceHeight * d, l.zRange * z * k - z), 815 | FSS.Vector3.add(s.position, s.anchor); 816 | H.dirty = !0; 817 | g(); 818 | requestAnimationFrame(h) 819 | } 820 | function g() { 821 | D.render(B); 822 | if (n.draw) { 823 | var b, 824 | d, 825 | g, 826 | h; 827 | for (b = B.lights.length - 1; 0 <= b; b--) switch (h = B.lights[b], d = h.position[0], g = h.position[1], m.renderer) { 828 | case s: 829 | D.context.lineWidth = 0.5; 830 | D.context.beginPath(); 831 | D.context.arc(d, g, 10, 0, Math.PIM2); 832 | D.context.strokeStyle = h.ambientHex; 833 | D.context.stroke(); 834 | D.context.beginPath(); 835 | D.context.arc(d, g, 4, 0, Math.PIM2); 836 | D.context.fillStyle = h.diffuseHex; 837 | D.context.fill(); 838 | break; 839 | case k: 840 | d += D.halfWidth, 841 | g = D.halfHeight - g, 842 | h.core.setAttributeNS(null, "fill", h.diffuseHex), 843 | h.core.setAttributeNS(null, "cx", d), 844 | h.core.setAttributeNS(null, "cy", g), 845 | D.element.appendChild(h.core), 846 | h.ring.setAttributeNS(null, "stroke", h.ambientHex), 847 | h.ring.setAttributeNS(null, "cx", d), 848 | h.ring.setAttributeNS(null, "cy", g), 849 | D.element.appendChild(h.ring) 850 | } 851 | } 852 | } 853 | var l = { 854 | width: 1.8, 855 | height: 1.8, 856 | depth: 10, 857 | segments: 16, 858 | slices: 8, 859 | xRange: 0.8, 860 | yRange: 0.1, 861 | zRange: 1, 862 | ambient: "#010101", 863 | diffuse: "#ffffff", 864 | speed: 6E-4, 865 | opacity: 0.5 866 | }, 867 | n = { 868 | count: 2, 869 | xyScalar: 1, 870 | zOffset: 100, 871 | ambient: "#ffffff", 872 | diffuse: "#2d2d2d", 873 | speed: 0.001, 874 | gravity: 800, 875 | dampening: 0.95, 876 | minLimit: 10, 877 | maxLimit: null, 878 | minDistance: 20, 879 | maxDistance: 400, 880 | autopilot: !1, 881 | draw: !1, 882 | bounds: FSS.Vector3.create(), 883 | step: FSS.Vector3.create(Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1)) 884 | }, 885 | s = "canvas", 886 | k = "svg", 887 | m = { 888 | renderer: s 889 | }, 890 | r, 891 | u = Date.now(), 892 | E = FSS.Vector3.create(), 893 | y = FSS.Vector3.create(), 894 | z = document.getElementById(container || "container"); 895 | document.getElementById("controls"); 896 | var X = document.getElementById(output || "output"); 897 | document.getElementById("ui"); 898 | var D, 899 | B, 900 | G, 901 | H, 902 | J, 903 | R, 904 | C, 905 | S; 906 | R = new FSS.WebGLRenderer; 907 | C = new FSS.CanvasRenderer; 908 | S = new FSS.SVGRenderer; (function(b) { 909 | D && X.removeChild(D.element); 910 | switch (b) { 911 | case "webgl": 912 | D = R; 913 | break; 914 | case s: 915 | D = C; 916 | break; 917 | case k: 918 | D = S 919 | } 920 | D.setSize(z.offsetWidth, z.offsetHeight); 921 | X.appendChild(D.element) 922 | })(m.renderer); 923 | B = new FSS.Scene; 924 | b(); (function() { 925 | var b, 926 | d; 927 | for (b = B.lights.length - 928 | 1; 0 <= b; b--) d = B.lights[b], 929 | B.remove(d); 930 | D.clear(); 931 | for (b = 0; b < n.count; b++) d = new FSS.Light(n.ambient, n.diffuse), 932 | d.ambientHex = d.ambient.format(), 933 | d.diffuseHex = d.diffuse.format(), 934 | B.add(d), 935 | d.mass = Math.randomInRange(0.5, 1), 936 | d.velocity = FSS.Vector3.create(), 937 | d.acceleration = FSS.Vector3.create(), 938 | d.force = FSS.Vector3.create(), 939 | d.ring = document.createElementNS(FSS.SVGNS, "circle"), 940 | d.ring.setAttributeNS(null, "stroke", d.ambientHex), 941 | d.ring.setAttributeNS(null, "stroke-width", "0.5"), 942 | d.ring.setAttributeNS(null, "fill", "none"), 943 | d.ring.setAttributeNS(null, "r", "10"), 944 | d.core = document.createElementNS(FSS.SVGNS, "circle"), 945 | d.core.setAttributeNS(null, "fill", d.diffuseHex), 946 | d.core.setAttributeNS(null, "r", "4") 947 | })(); 948 | window.addEventListener("resize", 949 | function(b) { 950 | d(z.offsetWidth, z.offsetHeight); 951 | g() 952 | }); 953 | z.addEventListener("click", 954 | function(b) { 955 | FSS.Vector3.set(y, b.x, D.height - b.y); 956 | FSS.Vector3.subtract(y, E); 957 | n.autopilot = !n.autopilot; (void 0).updateDisplay() 958 | }); 959 | z.addEventListener("mousemove", 960 | function(b) { 961 | FSS.Vector3.set(y, b.x, D.height - b.y); 962 | FSS.Vector3.subtract(y, E) 963 | }); 964 | d(z.offsetWidth, z.offsetHeight); 965 | h() 966 | } (); 967 | 968 | } -------------------------------------------------------------------------------- /js/vector.js: -------------------------------------------------------------------------------- 1 | CAV={FRONT:0,BACK:1,DOUBLE:2,SVGNS:"http://www.w3.org/2000/svg"};CAV.Array=typeof Float32Array==="function"?Float32Array:Array;CAV.Utils={isNumber:function(a){return!isNaN(parseFloat(a))&&isFinite(a)}}; 2 | (function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;cb&&(a[0]=b);a[1]>b&&(a[1]=b);a[2]>b&&(a[2]=b);return this},clamp:function(a,b,c){this.min(a,b);this.max(a,c);return this},limit:function(a,b,c){var d=this.length(a);b!==null&&dc&&this.setLength(a,c);return this},dot:function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]},normalise:function(a){return this.divideScalar(a,this.length(a))},negate:function(a){return this.multiplyScalar(a, 8 | -1)},distanceSquared:function(a,b){var c=a[0]-b[0],d=a[1]-b[1],e=a[2]-b[2];return c*c+d*d+e*e},distance:function(a,b){return Math.sqrt(this.distanceSquared(a,b))},lengthSquared:function(a){return a[0]*a[0]+a[1]*a[1]+a[2]*a[2]},length:function(a){return Math.sqrt(this.lengthSquared(a))},setLength:function(a,b){var c=this.length(a);c!==0&&b!==c&&this.multiplyScalar(a,b/c);return this}}; 9 | CAV.Vector4={create:function(a,b,c){var d=new CAV.Array(4);this.set(d,a,b,c);return d},set:function(a,b,c,d,e){a[0]=b||0;a[1]=c||0;a[2]=d||0;a[3]=e||0;return this},setX:function(a,b){a[0]=b||0;return this},setY:function(a,b){a[1]=b||0;return this},setZ:function(a,b){a[2]=b||0;return this},setW:function(a,b){a[3]=b||0;return this},add:function(a,b){a[0]+=b[0];a[1]+=b[1];a[2]+=b[2];a[3]+=b[3];return this},multiplyVectors:function(a,b,c){a[0]=b[0]*c[0];a[1]=b[1]*c[1];a[2]=b[2]*c[2];a[3]=b[3]*c[3];return this}, 10 | multiplyScalar:function(a,b){a[0]*=b;a[1]*=b;a[2]*=b;a[3]*=b;return this},min:function(a,b){a[0]b&&(a[0]=b);a[1]>b&&(a[1]=b);a[2]>b&&(a[2]=b);a[3]>b&&(a[3]=b);return this},clamp:function(a,b,c){this.min(a,b);this.max(a,c);return this}};CAV.Color=function(a,b){this.rgba=CAV.Vector4.create();this.hex=a||"#000000";this.opacity=CAV.Utils.isNumber(b)?b:1;this.set(this.hex,this.opacity)}; 11 | CAV.Color.prototype={set:function(a,b){var a=a.replace("#",""),c=a.length/3;this.rgba[0]=parseInt(a.substring(c*0,c*1),16)/255;this.rgba[1]=parseInt(a.substring(c*1,c*2),16)/255;this.rgba[2]=parseInt(a.substring(c*2,c*3),16)/255;this.rgba[3]=CAV.Utils.isNumber(b)?b:this.rgba[3];return this},hexify:function(a){a=Math.ceil(a*255).toString(16);a.length===1&&(a="0"+a);return a},format:function(){var a=this.hexify(this.rgba[0]),b=this.hexify(this.rgba[1]),c=this.hexify(this.rgba[2]);return this.hex="#"+ 12 | a+b+c}};CAV.Object=function(){this.position=CAV.Vector3.create()};CAV.Object.prototype={setPosition:function(a,b,c){CAV.Vector3.set(this.position,a,b,c);return this}};CAV.Light=function(a,b){CAV.Object.call(this);this.ambient=new CAV.Color(a||"#FFFFFF");this.diffuse=new CAV.Color(b||"#FFFFFF");this.ray=CAV.Vector3.create()};CAV.Light.prototype=Object.create(CAV.Object.prototype);CAV.Vertex=function(a,b,c){this.position=CAV.Vector3.create(a,b,c)}; 13 | CAV.Vertex.prototype={setPosition:function(a,b,c){CAV.Vector3.set(this.position,a,b,c);return this}}; 14 | CAV.Triangle=function(a,b,c){this.a=a||new CAV.Vertex;this.b=b||new CAV.Vertex;this.c=c||new CAV.Vertex;this.vertices=[this.a,this.b,this.c];this.u=CAV.Vector3.create();this.v=CAV.Vector3.create();this.centroid=CAV.Vector3.create();this.normal=CAV.Vector3.create();this.color=new CAV.Color;this.polygon=document.createElementNS(CAV.SVGNS,"polygon");this.polygon.setAttributeNS(null,"stroke-linejoin","round");this.polygon.setAttributeNS(null,"stroke-miterlimit","1");this.polygon.setAttributeNS(null,"stroke-width", 15 | "1");this.computeCentroid();this.computeNormal()}; 16 | CAV.Triangle.prototype={computeCentroid:function(){this.centroid[0]=this.a.position[0]+this.b.position[0]+this.c.position[0];this.centroid[1]=this.a.position[1]+this.b.position[1]+this.c.position[1];this.centroid[2]=this.a.position[2]+this.b.position[2]+this.c.position[2];CAV.Vector3.divideScalar(this.centroid,3);return this},computeNormal:function(){CAV.Vector3.subtractVectors(this.u,this.b.position,this.a.position);CAV.Vector3.subtractVectors(this.v,this.c.position,this.a.position);CAV.Vector3.crossVectors(this.normal, 17 | this.u,this.v);CAV.Vector3.normalise(this.normal);return this}};CAV.Geometry=function(){this.vertices=[];this.triangles=[];this.dirty=false};CAV.Geometry.prototype={update:function(){if(this.dirty){var a,b;for(a=this.triangles.length-1;a>=0;a--)b=this.triangles[a],b.computeCentroid(),b.computeNormal();this.dirty=false}return this}}; 18 | CAV.Plane=function(a,b,c,d){CAV.Geometry.call(this);this.width=a||100;this.height=b||100;this.segments=c||4;this.slices=d||4;this.segmentWidth=this.width/this.segments;this.sliceHeight=this.height/this.slices;var e,f,g,c=[];e=this.width*-0.5;f=this.height*0.5;for(a=0;a<=this.segments;a++){c.push([]);for(b=0;b<=this.slices;b++)d=new CAV.Vertex(e+a*this.segmentWidth,f-b*this.sliceHeight),c[a].push(d),this.vertices.push(d)}for(a=0;a=0;c--){d=this.geometry.triangles[c];CAV.Vector4.set(d.color.rgba);for(e=a.length-1;e>=0;e--)f=a[e],CAV.Vector3.subtractVectors(f.ray,f.position,d.centroid),CAV.Vector3.normalise(f.ray),g=CAV.Vector3.dot(d.normal,f.ray),this.side===CAV.FRONT?g=Math.max(g,0):this.side===CAV.BACK?g=Math.abs(Math.min(g,0)):this.side===CAV.DOUBLE&&(g=Math.max(Math.abs(g),0)),CAV.Vector4.multiplyVectors(this.material.slave.rgba, 21 | this.material.ambient.rgba,f.ambient.rgba),CAV.Vector4.add(d.color.rgba,this.material.slave.rgba),CAV.Vector4.multiplyVectors(this.material.slave.rgba,this.material.diffuse.rgba,f.diffuse.rgba),CAV.Vector4.multiplyScalar(this.material.slave.rgba,g),CAV.Vector4.add(d.color.rgba,this.material.slave.rgba);CAV.Vector4.clamp(d.color.rgba,0,1)}return this};CAV.Scene=function(){this.meshes=[];this.lights=[]}; 22 | CAV.Scene.prototype={add:function(a){a instanceof CAV.Mesh&&!~this.meshes.indexOf(a)?this.meshes.push(a):a instanceof CAV.Light&&!~this.lights.indexOf(a)&&this.lights.push(a);return this},remove:function(a){a instanceof CAV.Mesh&&~this.meshes.indexOf(a)?this.meshes.splice(this.meshes.indexOf(a),1):a instanceof CAV.Light&&~this.lights.indexOf(a)&&this.lights.splice(this.lights.indexOf(a),1);return this}};CAV.Renderer=function(){this.halfHeight=this.halfWidth=this.height=this.width=0}; 23 | CAV.Renderer.prototype={setSize:function(a,b){if(!(this.width===a&&this.height===b))return this.width=a,this.height=b,this.halfWidth=this.width*0.5,this.halfHeight=this.height*0.5,this},clear:function(){return this},render:function(){return this}};CAV.CanvasRenderer=function(){CAV.Renderer.call(this);this.element=document.createElement("canvas");this.element.style.display="block";this.context=this.element.getContext("2d");this.setSize(this.element.width,this.element.height)}; 24 | CAV.CanvasRenderer.prototype=Object.create(CAV.Renderer.prototype);CAV.CanvasRenderer.prototype.setSize=function(a,b){CAV.Renderer.prototype.setSize.call(this,a,b);this.element.width=a;this.element.height=b;this.context.setTransform(1,0,0,-1,this.halfWidth,this.halfHeight);return this};CAV.CanvasRenderer.prototype.clear=function(){CAV.Renderer.prototype.clear.call(this);this.context.clearRect(-this.halfWidth,-this.halfHeight,this.width,this.height);return this}; 25 | CAV.CanvasRenderer.prototype.render=function(a){CAV.Renderer.prototype.render.call(this,a);var b,c,d,e,f;this.clear();this.context.lineJoin="round";this.context.lineWidth=1;for(b=a.meshes.length-1;b>=0;b--)if(c=a.meshes[b],c.visible){c.update(a.lights,true);for(d=c.geometry.triangles.length-1;d>=0;d--)e=c.geometry.triangles[d],f=e.color.format(),this.context.beginPath(),this.context.moveTo(e.a.position[0],e.a.position[1]),this.context.lineTo(e.b.position[0],e.b.position[1]),this.context.lineTo(e.c.position[0], 26 | e.c.position[1]),this.context.closePath(),this.context.strokeStyle=f,this.context.fillStyle=f,this.context.stroke(),this.context.fill()}return this}; 27 | 28 | function Victor(container, anitOut){ 29 | if (!!document.createElement("canvas").getContext) { 30 | var t = { 31 | width: 1.5, 32 | height: 1.5, 33 | depth: 10, 34 | segments: 12, 35 | slices: 6, 36 | xRange: 0.8, 37 | yRange: 0.1, 38 | zRange: 1, 39 | ambient: "#525252", 40 | diffuse: "#FFFFFF", 41 | speed: 0.0002 42 | }; 43 | var G = { 44 | count: 2, 45 | xyScalar: 1, 46 | zOffset: 100, 47 | ambient: "#002c4a", 48 | diffuse: "#005584", 49 | speed: 0.001, 50 | gravity: 1200, 51 | dampening: 0.95, 52 | minLimit: 10, 53 | maxLimit: null, 54 | minDistance: 20, 55 | maxDistance: 400, 56 | autopilot: false, 57 | draw: false, 58 | bounds: CAV.Vector3.create(), 59 | step: CAV.Vector3.create(Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1)) 60 | }; 61 | var m = "canvas"; 62 | var E = "svg"; 63 | var x = { 64 | renderer: m 65 | }; 66 | var i, n = Date.now(); 67 | var L = CAV.Vector3.create(); 68 | var k = CAV.Vector3.create(); 69 | var z = document.getElementById(container || "container"); 70 | var w = document.getElementById(anitOut || "anitOut"); 71 | var D, I, h, q, y; 72 | var g; 73 | var r; 74 | 75 | function C() { 76 | F(); 77 | p(); 78 | s(); 79 | B(); 80 | v(); 81 | K(z.offsetWidth, z.offsetHeight); 82 | o() 83 | } 84 | 85 | function F() { 86 | g = new CAV.CanvasRenderer(); 87 | H(x.renderer) 88 | } 89 | 90 | function H(N) { 91 | if (D) { 92 | w.removeChild(D.element) 93 | } 94 | switch (N) { 95 | case m: 96 | D = g; 97 | break 98 | } 99 | D.setSize(z.offsetWidth, z.offsetHeight); 100 | w.appendChild(D.element) 101 | } 102 | 103 | function p() { 104 | I = new CAV.Scene() 105 | } 106 | 107 | function s() { 108 | I.remove(h); 109 | D.clear(); 110 | q = new CAV.Plane(t.width * D.width, t.height * D.height, t.segments, t.slices); 111 | y = new CAV.Material(t.ambient, t.diffuse); 112 | h = new CAV.Mesh(q, y); 113 | I.add(h); 114 | var N, O; 115 | for (N = q.vertices.length - 1; N >= 0; N--) { 116 | O = q.vertices[N]; 117 | O.anchor = CAV.Vector3.clone(O.position); 118 | O.step = CAV.Vector3.create(Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1)); 119 | O.time = Math.randomInRange(0, Math.PIM2) 120 | } 121 | } 122 | 123 | function B() { 124 | var O, N; 125 | for (O = I.lights.length - 1; O >= 0; O--) { 126 | N = I.lights[O]; 127 | I.remove(N) 128 | } 129 | D.clear(); 130 | for (O = 0; O < G.count; O++) { 131 | N = new CAV.Light(G.ambient, G.diffuse); 132 | N.ambientHex = N.ambient.format(); 133 | N.diffuseHex = N.diffuse.format(); 134 | I.add(N); 135 | N.mass = Math.randomInRange(0.5, 1); 136 | N.velocity = CAV.Vector3.create(); 137 | N.acceleration = CAV.Vector3.create(); 138 | N.force = CAV.Vector3.create() 139 | } 140 | } 141 | 142 | function K(O, N) { 143 | D.setSize(O, N); 144 | CAV.Vector3.set(L, D.halfWidth, D.halfHeight); 145 | s() 146 | } 147 | 148 | function o() { 149 | i = Date.now() - n; 150 | u(); 151 | M(); 152 | requestAnimationFrame(o) 153 | } 154 | 155 | function u() { 156 | var Q, P, O, R, T, V, U, S = t.depth / 2; 157 | CAV.Vector3.copy(G.bounds, L); 158 | CAV.Vector3.multiplyScalar(G.bounds, G.xyScalar); 159 | CAV.Vector3.setZ(k, G.zOffset); 160 | for (R = I.lights.length - 1; R >= 0; R--) { 161 | T = I.lights[R]; 162 | CAV.Vector3.setZ(T.position, G.zOffset); 163 | var N = Math.clamp(CAV.Vector3.distanceSquared(T.position, k), G.minDistance, G.maxDistance); 164 | var W = G.gravity * T.mass / N; 165 | CAV.Vector3.subtractVectors(T.force, k, T.position); 166 | CAV.Vector3.normalise(T.force); 167 | CAV.Vector3.multiplyScalar(T.force, W); 168 | CAV.Vector3.set(T.acceleration); 169 | CAV.Vector3.add(T.acceleration, T.force); 170 | CAV.Vector3.add(T.velocity, T.acceleration); 171 | CAV.Vector3.multiplyScalar(T.velocity, G.dampening); 172 | CAV.Vector3.limit(T.velocity, G.minLimit, G.maxLimit); 173 | CAV.Vector3.add(T.position, T.velocity) 174 | } 175 | for (V = q.vertices.length - 1; V >= 0; V--) { 176 | U = q.vertices[V]; 177 | Q = Math.sin(U.time + U.step[0] * i * t.speed); 178 | P = Math.cos(U.time + U.step[1] * i * t.speed); 179 | O = Math.sin(U.time + U.step[2] * i * t.speed); 180 | CAV.Vector3.set(U.position, t.xRange * q.segmentWidth * Q, t.yRange * q.sliceHeight * P, t.zRange * S * O - S); 181 | CAV.Vector3.add(U.position, U.anchor) 182 | } 183 | q.dirty = true 184 | } 185 | 186 | function M() { 187 | D.render(I) 188 | } 189 | 190 | function J(O) { 191 | var Q, N, S = O; 192 | var P = function(T) { 193 | for (Q = 0, l = I.lights.length; Q < l; Q++) { 194 | N = I.lights[Q]; 195 | N.ambient.set(T); 196 | N.ambientHex = N.ambient.format() 197 | } 198 | }; 199 | var R = function(T) { 200 | for (Q = 0, l = I.lights.length; Q < l; Q++) { 201 | N = I.lights[Q]; 202 | N.diffuse.set(T); 203 | N.diffuseHex = N.diffuse.format() 204 | } 205 | }; 206 | return { 207 | set: function() { 208 | P(S[0]); 209 | R(S[1]) 210 | } 211 | } 212 | } 213 | 214 | function v() { 215 | window.addEventListener("resize", j) 216 | } 217 | 218 | function A(N) { 219 | CAV.Vector3.set(k, N.x, D.height - N.y); 220 | CAV.Vector3.subtract(k, L) 221 | } 222 | 223 | function j(N) { 224 | K(z.offsetWidth, z.offsetHeight); 225 | M() 226 | } 227 | C(); 228 | } 229 | return J; 230 | } --------------------------------------------------------------------------------