超酷HTML5 3D线条凹凸人物头像动画特效《附上领取方式》

特效展示

领取方式:看我主页简介可领取源码


css源码展示​:

html {

touch-action: none;

content-zooming: none;

}

body {

position: absolute;

margin: 0;

padding: 0;

background: #000;

width: 100%;

height: 100%;

}

#canvas {

width: 100%; 

height: 100%;

background: #000;

}

#source {

visibility: hidden;

width:0;

height:0;

}

js源码展示:

// Inspired by https://www.airtightinteractive.com/2011/06/rutt-etra-izer/

"use strict";

{

// webGL canvas

const canvas = {

init(options) {

// set webGL context

this.elem = document.querySelector("canvas");

const gl = (this.gl =

this.elem.getContext("webgl", options) ||

this.elem.getContext("experimental-webgl", options));

if (!gl) return false;

// compile shaders

const vertexShader = gl.createShader(gl.VERTEX_SHADER);

gl.shaderSource(

vertexShader,

`

precision highp float;

const float FOV = 250.0;

attribute vec3 aPosition, aColor;

uniform vec2 uResolution;

uniform vec4 uRotation;

varying vec3 color;

void main() {

// 3D Rotations

float tx, ty, tz;

tx = uRotation.z * aPosition.x - uRotation.w * aPosition.z;

tz = uRotation.w * aPosition.x + uRotation.z * aPosition.z;

ty = uRotation.x * aPosition.y - uRotation.y * tz;

tz = uRotation.y * aPosition.y + uRotation.x * tz;

// 3D to 2D projection

float s = FOV / (FOV - tz);

float x = s > 0.0 ? tx * s : 0.0;

float y = s > 0.0 ? ty * s : 0.0;

gl_Position = vec4(

( (uResolution.x * 0.5 + x) / uResolution.x * 2.0) - 1.0,

( (-uResolution.y * 0.5 -y) / uResolution.y * 2.0) + 1.0,

s > 0.0 ? 0.0 : 10.0,

1.0

);

color = aColor;

}

  `

);

gl.compileShader(vertexShader);

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

gl.shaderSource(

fragmentShader,

`

precision highp float;

varying vec3 color;

void main() {

gl_FragColor = vec4(color.r, color.g, color.b, 1.0);

}

`

);

gl.compileShader(fragmentShader);

const program = (this.program = gl.createProgram());

gl.attachShader(this.program, vertexShader);

gl.attachShader(this.program, fragmentShader);

gl.linkProgram(this.program);

gl.useProgram(this.program);

// resolution

this.uResolution = gl.getUniformLocation(this.program, "uResolution");

gl.enableVertexAttribArray(this.uResolution);

// canvas resize

this.resize();

window.addEventListener("resize", () => this.resize(), false);

return gl;

},

buffer(attribute) {

const buffer = {

attribute: gl.getAttribLocation(canvas.program, attribute),

buffer: gl.createBuffer(),

load (data, usage, size) {

gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);

gl.vertexAttribPointer(this.attribute, size, gl.FLOAT, false, 0, 0);

gl.bufferData(gl.ARRAY_BUFFER, data, usage);

}

};

gl.enableVertexAttribArray(buffer.attribute);

return buffer;

},

resize() {

this.width = this.elem.width = this.elem.offsetWidth;

this.height = this.elem.height = this.elem.offsetHeight;

this.gl.uniform2f(this.uResolution, this.width, this.height);

this.gl.viewport(

0,

0,

this.gl.drawingBufferWidth,

this.gl.drawingBufferHeight

);

}

};

// init pointer

const pointer = {

init(canvas) {

this.x = canvas.width * 0.5;

this.y = canvas.height * 0.5;

this.ex = this.x;

this.ey = this.y * 2;

["mousemove", "touchstart", "touchmove"].forEach((event, touch) => {

document.addEventListener(

event,

e => {

if (touch) {

e.preventDefault();

this.x = e.targetTouches[0].clientX;

this.y = e.targetTouches[0].clientY;

} else {

this.x = e.clientX;

this.y = e.clientY;

}

},

false

);

});

},

ease(step) {

this.ex += (this.x - this.ex) * step;

this.ey += (this.y - this.ey) * step;

}

};

// init webGL canvas

const gl = canvas.init({

alpha: false,

stencil: false,

antialias: true,

depth: false

});

// additive blending "lighter"

gl.blendFunc(gl.SRC_ALPHA, gl.ONE);

gl.enable(gl.BLEND);

// init pointer

pointer.init(canvas);

// init image

const img = new Image();

img.addEventListener("load", e => load(img));

img.src = document.getElementById("source").src;

// rotation matrix

const uRotation = gl.getUniformLocation(canvas.program, "uRotation");

gl.enableVertexAttribArray(uRotation);

// init colors and geometry

let nVertices = 0;

const load = img => {

//////////////////////

const step = 3;

const depth = 75;

//////////////////////

const image = document.createElement("canvas");

const width = (image.width = img.width);

const height = (image.height = img.height);

const ctx = image.getContext("2d");

ctx.drawImage(img, 0, 0);

const bitmap = ctx.getImageData(0, 0, width, height).data;

const vertices = [];

const colors = [];

// buffers

const position = canvas.buffer("aPosition");

const color = canvas.buffer("aColor");

// push vertex

const push = (x, y) => {

const p = (y * width + x) * 4;

const r = bitmap[p + 0] / 256;

const g = bitmap[p + 1] / 256;

const b = bitmap[p + 2] / 256;

colors.push(r);

colors.push(g);

colors.push(b);

vertices.push(x - width * 0.5);

vertices.push(y - height * 0.5);

vertices.push(depth * (0.34 * r + 0.5 * g + 0.16 * b));

};

// init the line

let d = 1;

for (let y = 0; y < height; y += step) {

if (d > 0) {

for (let x = 0; x < width; x += step) push(x, y);

} else {

for (let x = width; x > 0; x -= step) push(x, y);

}

d = -d;

}

// load buffers to gpu

nVertices = Math.floor(colors.length / 3);

position.load(new Float32Array(vertices), gl.STATIC_DRAW, 3);

color.load(new Float32Array(colors), gl.STATIC_DRAW, 3);

// start animation loop

requestAnimationFrame(run);

};

// main animation loop

const run = () => {

requestAnimationFrame(run);

pointer.ease(0.05);

// rotation

const ry = (pointer.ex - canvas.width * 0.5) / (canvas.width * 0.33);

const rx = (pointer.ey - canvas.height * 0.5) / (canvas.height * 0.33);

gl.uniform4f(uRotation, Math.cos(rx), Math.sin(rx), Math.cos(ry), Math.sin(ry));

// draw line strip

gl.drawArrays(gl.LINE_STRIP, 0, nVertices);

};

}

如果你想学习web前端可以来我的web前端直播学习群:912443227关注+转发+加群就能免费领取一套web前端资料,每晚8点群里直播讲解,

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容