以下是 HTML5 Canvas文本输入框动画特效代码 的示例演示效果:
部分效果截图:
HTML代码(index.html):
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HTML5 Canvas文本输入框动画特效</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<input type="text" placeholder="请输入关键词">
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>
JS代码(index.js):
console.clear();
var MAX_LIFE = 50;
var canvas = document.querySelector('canvas');
var input = document.querySelector('input');
var field ={
}
var hasFocus = false;
var caret = document.createElement('span');
caret.style.cssText = document.defaultView.getComputedStyle(input,'').cssText;
caret.style.position = 'absolute';
caret.style.left = 0;
caret.style.top = 0;
caret.style.width = 'auto';
caret.style.visibility = 'hidden';
document.body.appendChild(caret);
function reposition(){
field = input.getBoundingClientRect();
}
window.onload = reposition;
window.onresize = reposition;
reposition();
input.onfocus = function(){
hasFocus = true}
input.onblur = function(){
hasFocus = false}
function rain(){
}
function burst(intensity){
var behavior = [this.behavior.cohesion(),this.behavior.move()];
var size = .75;
``var force = .7;
var lifeMin = 0;
var progress = Math.min(field.width,caret.offsetWidth) / field.width;
var offset = field.left + (field.width * progress);
var rangeMin = Math.max(field.left,offset - 30);
var rangeMax = Math.min(field.right,offset + 10);
this.spray(intensity,function(){
return [null,null,Vector.create(Random.between(rangeMin + 10,rangeMax - 20),Random.between(field.top + 15,field.bottom - 15)),Vector.random(force),size + Math.random(),Random.between(lifeMin,0),behavior]}
);
// top edgethis.spray(intensity * .5,function(){
return [null,null,Vector.create(Random.between(rangeMin,rangeMax),field.top),Vector.random(force),size + Math.random(),Random.between(lifeMin,0),behavior]}
);
// bottom edgethis.spray(intensity * .5,function(){
return [null,null,Vector.create(Random.between(rangeMin,rangeMax),field.top + field.height),Vector.random(force),size + Math.random(),Random.between(lifeMin,0),behavior]}
);
// left edgeif (input.value.length === 1){
this.spray(intensity * 2,function(){
return [null,null,Vector.create(field.left,Random.between(field.top,field.bottom)),Vector.random(force),size + Math.random(),Random.between(lifeMin,0),behavior]}
);
}
// right edgeif (rangeMax == field.right){
this.spray(intensity * 2,function(){
return [null,null,Vector.create(field.right,Random.between(field.top,field.bottom)),Vector.random(force),size + Math.random(),Random.between(lifeMin,0),behavior]}
);
}
}
// start particle simulationsimulate('2d',{
init:function(){
}
,tick:function(particles){
if (!particles){
return;
}
particles.forEach(function(p){
if (p.life > MAX_LIFE){
this.destroy(p);
}
}
);
}
,beforePaint:function(){
this.clear();
}
,paint:function(particle){
var p = particle.position;
var s = particle.size;
var o = 1 - (particle.life / MAX_LIFE);
this.paint.circle(p.x,p.y,s,'rgba(255,255,255,' + o + ')');
this.paint.circle(p.x,p.y,s + 2,'rgba(231,244,255,' + (o * .25) + ')');
}
,afterPaint:function(){
// nothing}
,action:function(x,y){
caret.textContent = input.value;
burst.call(this,10);
input.classList.add('keyup');
setTimeout(function(){
input.classList.remove('keyup')}
,100);
}
}
);
// "simulate" particle simulation logic/** * Constants */
PI_2 = Math.PI / 2;
PI_180 = Math.PI / 180;
/** * Random */
var Random ={
between:function(min,max){
return min + (Math.random() * (max - min));
}
}
/** * 2D Vector Class */
function Vector(x,y){
this._x = x || 0;
this._y = y || 0;
}
Vector.create = function(x,y){
return new Vector(x,y);
}
;
Vector.add = function(a,b){
return new Vector(a.x + b.x,a.y + b.y);
}
;
Vector.subtract = function(a,b){
return new Vector(a.x - b.x,a.y - b.y);
}
;
Vector.random = function(range){
var v = new Vector();
v.randomize(range);
return v;
}
;
Vector.distanceSquared = function(a,b){
var dx = a.x - b.x;
var dy = a.y - b.y;
return dx * dx + dy * dy;
}
;
Vector.distance = function(a,b){
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
;
Vector.prototype ={
get x(){
return this._x;
}
,get y(){
return this._y;
}
,set x(value){
this._x = value;
}
,set y(value){
this._y = value;
}
,get magnitudeSquared(){
return this._x * this._x + this._y * this._y;
}
,get magnitude(){
return Math.sqrt(this.magnitudeSquared);
}
,get angle(){
return Math.atan2(this._y,this._x) * 180 / Math.PI;
}
,clone:function(){
return new Vector(this._x,this._y);
}
,add:function(v){
this._x += v.x;
this._y += v.y;
}
,subtract:function(v){
this._x -= v.x;
this._y -= v.y;
}
,multiply:function(value){
this._x *= value;
this._y *= value;
}
,divide:function(value){
this._x /= value;
this._y /= value;
}
,normalize:function(){
var magnitude = this.magnitude;
if (magnitude > 0){
this.divide(magnitude);
}
}
,limit:function(treshold){
if (this.magnitude > treshold){
this.normalize();
this.multiply(treshold);
}
}
,randomize:function(amount){
amount = amount || 1;
this._x = amount * 2 * (-.5 + Math.random());
this._y = amount * 2 * (-.5 + Math.random());
}
,rotate:function(degrees){
var magnitude = this.magnitude;
var angle = ((Math.atan2(this._x,this._y) * PI_HALF) + degrees) * PI_180;
this._x = magnitude * Math.cos(angle);
this._y = magnitude * Math.sin(angle);
}
,flip:function(){
var temp = this._y;
this._y = this._x;
this._x = temp;
}
,invert:function(){
this._x = -this._x;
this._y = -this._y;
}
,toString:function(){
return this._x + ',' + this._y;
}
}
/** * Particle Class */
function Particle(id,group,position,velocity,size,life,behavior){
this._id = id || 'default';
this._group = group || 'default';
this._position = position || new Vector();
this._velocity = velocity || new Vector();
this._size = size || 1;
this._life = Math.round(life || 0);
this._behavior = behavior || [];
}
Particle.prototype ={
get id(){
return this._id;
}
,get group(){
return this._group;
}
,get life(){
return this._life;
}
,get size(){
return this._size;
}
,set size(size){
this._size = size;
}
,get position(){
return this._position;
}
,get velocity(){
return this._velocity;
}
,update:function(stage){
this._life++;
var i = 0;
var l = this._behavior.length;
for (;
i < l;
i++){
this._behavior[i].call(stage,this);
}
}
,toString:function(){
return 'Particle(' + this._id + ') ' + this._life + ' pos:' + this._position + ' vec:' + this._velocity;
}
}
// setup DOMfunction simulate(dimensions,options){
// private varsvar particles = [];
var destroyed = [];
var update = update || function(){
}
;
var stage = stage || function(){
}
;
var canvas;
var context;
if (!options){
console.error('"options" object must be defined');
return;
}
if (!options.init){
console.error('"init" function must be defined');
return;
}
if (!options.paint){
console.error('"paint" function must be defined');
return;
}
if (!options.tick){
options.tick = function(){
}
;
}
if (!options.beforePaint){
options.beforePaint = function(){
}
;
}
if (!options.afterPaint){
options.afterPaint = function(){
}
;
}
if (!options.action){
options.action = function(){
}
;
}
if (document.readyState === 'interactive'){
setup();
}
else{
document.addEventListener('DOMContentLoaded',setup);
}
// resizes canvas to fit window dimensionsfunction fitCanvas(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
// create canvas for drawingfunction setup(){
// createcanvas = document.createElement('canvas');
document.body.appendChild(canvas);
// correct canvas size on window resizewindow.addEventListener('resize',fitCanvas);
// gogo();
}
// canvas has been attached,let's go!function go(){
// set initial canvas sizefitCanvas();
// get context for drawingcontext = canvas.getContext(dimensions);
// simulation update loopfunction act(){
// update particle statesvar i = 0;
var l = particles.length;
var p;
for (;
i < l;
i++){
particles[i].update(this);
}
// clean destroyed particleswhile (p = destroyed.pop()){
do{
// has not been found in destroyed array?if (p !== particles[i]){
continue;
}
// remove particleparticles.splice(i,1);
}
while (i-- >= 0)}
// repaint contextoptions.beforePaint.call(this);
// repaint particlesi = 0;
l = particles.length;
for (;
i < l;
i++){
options.paint.call(this,particles[i]);
}
// after particles have been paintedoptions.afterPaint.call(this);
}
function tick(){
// call update method,this allows for inserting particles later onoptions.tick.call(this,particles);
// update particles hereact();
// on to the next framewindow.requestAnimationFrame(tick);
}
/** * API **/
function clear(){
context.clearRect(0,0,canvas.width,canvas.height);
}
function destroy(particle){
destroyed.push(particle);
}
function add(id,group,position,velocity,size,life,behavior){
particles.push(new Particle(id,group,position,velocity,size,life,behavior));
}
function spray(amount,config){
var i = 0;
for (;
i < amount;
i++){
add.apply(this,config());
}
}
function debug(particle){
this.paint.circle(particle.position.x,particle.position.y,particle.size,'rgba(255,0,0,.75)');
context.beginPath();
context.moveTo(particle.position.x,particle.position.y);
context.lineTo(particle.position.x + (particle.velocity.x * 10),particle.position.y + (particle.velocity.y * 10));
context.strokeStyle = 'rgba(255,0,0,.1)';
context.stroke();
context.closePath();
}
;
this.clear = clear;
this.destroy = destroy;
this.add = add;
this.spray = spray;
this.debug = debug;
this.paint ={
circle:function(x,y,size,color){
context.beginPath();
context.arc(x,y,size,0,2 * Math.PI,false);
context.fillStyle = color;
context.fill();
}
,square:function(x,y,size,color){
context.beginPath();
context.rect(x - (size * .5),y - (size * .5),size,size);
context.fillStyle = color;
context.fill();
}
}
this.behavior ={
cohesion:function(range,speed){
range = Math.pow(range || 100,2);
speed = speed || .001;
return function(particle){
var center = new Vector();
var i = 0;
var l = particles.length;
var count = 0;
if (l <= 1){
return;
}
for (;
i < l;
i++){
// don't use self in groupif (particles[i] === particle || Vector.distanceSquared(particles[i].position,particle.position) > range){
continue;
}
center.add(Vector.subtract(particles[i].position,particle.position));
count++;
}
if (count > 0){
center.divide(count);
center.normalize();
center.multiply(particle.velocity.magnitude);
center.multiply(.05);
}
particle.velocity.add(center);
}
}
,separation:function(distance){
var distance = Math.pow(distance || 25,2);
return function(particle){
var heading = new Vector();
var i = 0;
var l = particles.length;
var count = 0;
var diff;
if (l <= 1){
return;
}
for (;
i < l;
i++){
// don't use self in groupif (particles[i] === particle || Vector.distanceSquared(particles[i].position,particle.position) > distance){
continue;
}
// stay away from neighboursdiff = Vector.subtract(particle.position,particles[i].position);
diff.normalize();
heading.add(diff);
count++;
}
if (count > 0){
// get averageheading.divide(count);
// make same length as current velocity (so particle won't speed up)heading.normalize();
heading.multiply(particle.velocity.magnitude);
// limit force to make particle movement smootherheading.limit(.1);
}
particle.velocity.add(heading);
}
}
,alignment:function(range){
range = Math.pow(range || 100,2);
return function(particle){
var i = 0;
var l = particles.length;
var count = 0;
var heading = new Vector();
if (l <= 1){
return;
}
for (;
i < l;
i++){
// don't use self in group also don't align when out of rangeif (particles[i] === particle || Vector.distanceSquared(particles[i].position,particle.position) > range){
continue;
}
heading.add(particles[i].velocity);
count++;
}
if (count > 0){
heading.divide(count);
heading.normalize();
heading.multiply(particle.velocity.magnitude);
// limitheading.multiply(.1);
}
particle.velocity.add(heading);
}
}
,move:function(){
return function(particle){
particle.position.add(particle.velocity);
// handle collisions?}
}
,eat:function(food){
food = food || [];
return function(particle){
var i = 0;
var l = particles.length;
var prey;
for (;
i < l;
i++){
prey = particles[i];
// can't eat itself,also,needs to be tastyif (prey === particle || food.indexOf(prey.group) === -1){
continue;
}
// calculate force vectorif (Vector.distanceSquared(particle.position,neighbour.position) < 2 && particle.size >= neighbour.size){
particle.size += neighbour.size;
destroy(neighbour);
}
}
}
}
,force:function(x,y){
return function(particle){
particle.velocity.x += x;
particle.velocity.y += y;
}
}
,limit:function(treshold){
return function(particle){
particle.velocity.limit(treshold);
}
}
,attract:function(forceMultiplier,groups){
forceMultiplier = forceMultiplier || 1;
groups = groups || [];
return function(particle){
// attract other particlesvar totalForce = new Vector(0,0);
var force = new Vector(0,0);
var i = 0;
var l = particles.length;
var distance;
var pull;
var attractor;
var grouping = groups.length;
for (;
i < l;
i++){
attractor = particles[i];
// can't be attracted by itself or mismatched groupsif (attractor === particle || (grouping && groups.indexOf(attractor.group) === -1)){
continue;
}
// calculate force vectorforce.x = attractor.position.x - particle.position.x;
force.y = attractor.position.y - particle.position.y;
distance = force.magnitude;
force.normalize();
// the bigger the attractor the more forceforce.multiply(attractor.size / distance);
totalForce.add(force);
}
totalForce.multiply(forceMultiplier);
particle.velocity.add(totalForce);
}
}
,wrap:function(margin){
return function(particle){
// move around when particle reaches edge of screenvar position = particle.position;
var radius = particle.size * .5;
if (position.x + radius > canvas.width + margin){
position.x = radius;
}
if (position.y + radius > canvas.height + margin){
position.y = radius;
}
if (position.x - radius < -margin){
position.x = canvas.width - radius;
}
if (position.y - radius < -margin){
position.y = canvas.height - radius;
}
}
}
,reflect:function(){
return function(particle){
// bounce from edgesvar position = particle.position;
var velocity = particle.velocity;
var radius = particle.size * .5;
if (position.x + radius > canvas.width){
velocity.x = -velocity.x;
}
if (position.y + radius > canvas.height){
velocity.y = -velocity.y;
}
if (position.x - radius < 0){
velocity.x = -velocity.x;
}
if (position.y - radius < 0){
velocity.y = -velocity.y;
}
}
}
,edge:function(action){
return function(particle){
var position = particle.position;
var velocity = particle.velocity;
var radius = particle.size * .5;
if (position.x + radius > canvas.width){
action(particle);
}
if (position.y + radius > canvas.height){
action(particle);
}
if (position.x - radius < 0){
action(particle);
}
if (position.y - radius < 0){
action(particle);
}
}
}
}
// publicObject.defineProperties(this,{
'particles':{
get:function(){
return particles;
}
}
,'width':{
get:function(){
return canvas.width;
}
}
,'height':{
get:function(){
return canvas.height;
}
}
,'context':{
get:function(){
return context;
}
}
}
);
// call init method so the scene can be setupoptions.init.call(this)// start tickingtick();
// start listening to eventsvar self = this;
document.addEventListener('keyup',function(e){
options.action.call(self,e.pageX,e.pageY);
}
);
}
}
;
CSS代码(style.css):
input{border:2px solid #5E6C77;font-size:1.5em;padding:.25em .5em .3125em;color:#5E6C77;border-radius:.25em;background:transparent;-webkit-transition:all .100s;transition:all .100s;}
input:focus{outline:none;color:#A2ACB3;border-color:#A2ACB3;}
input.keyup{color:white;border-color:white;text-shadow:0 0 .125em white;box-shadow:0 0 .25em white,inset 0 0 .25em white;}
body{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}
canvas{position:absolute;left:0;right:0;top:0;bottom:0;pointer-events:none;}
input{font-family:"Arial Rounded MT Bold","Helvetica Rounded",Arial,sans-serif;}
::-webkit-input-placeholder{color:#5E6C77;text-shadow:0 0 .125em transparent;-webkit-transition:all .25s;transition:all .25s;}
input:focus::-webkit-input-placeholder{opacity:.5;}
::-moz-placeholder{color:#5E6C77;text-shadow:0 0 .125em transparent;-webkit-transition:all .25s;transition:all .25s;}
input:focus::-moz-placeholder{opacity:.5;}
:-ms-input-placeholder{color:#5E6C77;text-shadow:0 0 .125em transparent;-webkit-transition:all .25s;transition:all .25s;}
input:focus:-ms-input-placeholder{opacity:.5;}
html,body{height:100%;overflow:hidden;}
html{background:#282E33;}