演示
完整HTML
<! doctype html >
< html>
< head>
< meta charset = " utf-8" >
< title> H5 Canvas雷电打出文字特效</ title>
< style>
.page-thunder-to-text {
position : relative;
overflow : hidden;
}
.page-thunder-to-text canvas {
display : block;
}
.page-thunder-to-text input {
position : absolute;
bottom : 50px;
left : 0;
right : 0;
display : block;
outline : none;
background-color : rgba ( 38, 50, 56, 0.2) ;
color : #ffffff;
border : none;
width : 50%;
min-width : 500px;
max-width : 100%;
margin : auto;
height : 60px;
line-height : 60px;
font-size : 40px;
padding : 0 20px;
}
.page-thunder-to-text input:hover, .page-thunder-to-text input:focus {
border : 1px solid rgba ( 38, 50, 56, 0.6) ;
}
.page-thunder-to-text input::-webkit-input-placeholder {
color : rgba ( 255, 255, 255, 0.1) ;
}
</ style>
</ head>
< body>
< div class = " page page-thunder-to-text" >
< input id = " input" type = " text" maxlength = " 24" placeholder = " I love you!" >
< canvas id = " canvas" > </ canvas>
</ div>
< script>
let canvas, ctx, w, h, thunder, text, particles, input;
function Thunder ( options ) {
options = options || { } ;
this . lifespan = options. lifespan || Math. round ( Math. random ( ) * 10 + 10 ) ;
this . maxlife = this . lifespan;
this . color = options. color || '#fefefe' ;
this . glow = options. glow || '#2323fe' ;
this . x = options. x || Math. random ( ) * w;
this . y = options. y || Math. random ( ) * h;
this . width = options. width || 2 ;
this . direct = options. direct || Math. random ( ) * Math. PI * 2 ;
this . max = options. max || Math. round ( Math. random ( ) * 10 + 20 ) ;
this . segments = [ ... new Array ( this . max) ] . map ( ( ) => {
return {
direct : this . direct + ( Math. PI * Math. random ( ) * 0.2 - 0.1 ) ,
length : Math. random ( ) * 20 + 80 ,
change : Math. random ( ) * 0.04 - 0.02
} ;
} ) ;
this . update = function ( index, array ) {
this . segments. forEach ( s => { ( s. direct += s. change) && Math. random ( ) > 0.96 && ( s. change *= - 1 ) } ) ;
( this . lifespan > 0 && this . lifespan-- ) || this . remove ( index, array) ;
}
this . render = function ( ctx ) {
if ( this . lifespan <= 0 ) return ;
ctx. beginPath ( ) ;
ctx. globalAlpha = this . lifespan / this . maxlife;
ctx. strokeStyle = this . color;
ctx. lineWidth = this . width;
ctx. shadowBlur = 32 ;
ctx. shadowColor = this . glow;
ctx. moveTo ( this . x, this . y) ;
let prev = { x : this . x, y : this . y } ;
this . segments. forEach ( s => {
const x = prev. x + Math. cos ( s. direct) * s. length;
const y = prev. y + Math. sin ( s. direct) * s. length;
prev = { x : x, y : y } ;
ctx. lineTo ( x, y) ;
} ) ;
ctx. stroke ( ) ;
ctx. closePath ( ) ;
ctx. shadowBlur = 0 ;
const strength = Math. random ( ) * 80 + 40 ;
const light = ctx. createRadialGradient ( this . x, this . y, 0 , this . x, this . y, strength) ;
light. addColorStop ( 0 , 'rgba(250, 200, 50, 0.6)' ) ;
light. addColorStop ( 0.1 , 'rgba(250, 200, 50, 0.2)' ) ;
light. addColorStop ( 0.4 , 'rgba(250, 200, 50, 0.06)' ) ;
light. addColorStop ( 0.65 , 'rgba(250, 200, 50, 0.01)' ) ;
light. addColorStop ( 0.8 , 'rgba(250, 200, 50, 0)' ) ;
ctx. beginPath ( ) ;
ctx. fillStyle = light;
ctx. arc ( this . x, this . y, strength, 0 , Math. PI * 2 ) ;
ctx. fill ( ) ;
ctx. closePath ( ) ;
}
this . remove = function ( index, array ) {
array. splice ( index, 1 ) ;
}
}
function Spark ( options ) {
options = options || { } ;
this . x = options. x || w * 0.5 ;
this . y = options. y || h * 0.5 ;
this . v = options. v || { direct : Math. random ( ) * Math. PI * 2 , weight : Math. random ( ) * 14 + 2 , friction : 0.88 } ;
this . a = options. a || { change : Math. random ( ) * 0.4 - 0.2 , min : this . v. direct - Math. PI * 0.4 , max : this . v. direct + Math. PI * 0.4 } ;
this . g = options. g || { direct : Math. PI * 0.5 + ( Math. random ( ) * 0.4 - 0.2 ) , weight : Math. random ( ) * 0.25 + 0.25 } ;
this . width = options. width || Math. random ( ) * 3 ;
this . lifespan = options. lifespan || Math. round ( Math. random ( ) * 20 + 40 ) ;
this . maxlife = this . lifespan;
this . color = options. color || '#feca32' ;
this . prev = { x : this . x, y : this . y } ;
this . update = function ( index, array ) {
this . prev = { x : this . x, y : this . y } ;
this . x += Math. cos ( this . v. direct) * this . v. weight;
this . x += Math. cos ( this . g. direct) * this . g. weight;
this . y += Math. sin ( this . v. direct) * this . v. weight;
this . y += Math. sin ( this . g. direct) * this . g. weight;
this . v. weight > 0.2 && ( this . v. weight *= this . v. friction) ;
this . v. direct += this . a. change;
( this . v. direct > this . a. max || this . v. direct < this . a. min) && ( this . a. change *= - 1 ) ;
this . lifespan > 0 && this . lifespan-- ;
this . lifespan <= 0 && this . remove ( index, array) ;
}
this . render = function ( ctx ) {
if ( this . lifespan <= 0 ) return ;
ctx. beginPath ( ) ;
ctx. globalAlpha = this . lifespan / this . maxlife;
ctx. strokeStyle = this . color;
ctx. lineWidth = this . width;
ctx. moveTo ( this . x, this . y) ;
ctx. lineTo ( this . prev. x, this . prev. y) ;
ctx. stroke ( ) ;
ctx. closePath ( ) ;
}
this . remove = function ( index, array ) {
array. splice ( index, 1 ) ;
}
}
function Particles ( options ) {
options = options || { } ;
this . max = options. max || Math. round ( Math. random ( ) * 10 + 10 ) ;
this . sparks = [ ... new Array ( this . max) ] . map ( ( ) => new Spark ( options) ) ;
this . update = function ( ) {
this . sparks. forEach ( ( s, i ) => s. update ( i, this . sparks) ) ;
}
this . render = function ( ctx ) {
this . sparks. forEach ( s => s. render ( ctx) ) ;
}
}
function Text ( options ) {
options = options || { } ;
const pool = document. createElement ( 'canvas' ) ;
const buffer = pool. getContext ( '2d' ) ;
pool. width = w;
buffer. fillStyle = '#000000' ;
buffer. fillRect ( 0 , 0 , pool. width, pool. height) ;
this . size = options. size || 100 ;
this . copy = ( options. copy || ` Hello! ` ) + ' ' ;
this . color = options. color || '#cd96fe' ;
this . delay = options. delay || 5 ;
this . basedelay = this . delay;
buffer. font = ` ${ this . size} px Comic Sans MS ` ;
this . bound = buffer. measureText ( this . copy) ;
this . bound. height = this . size * 1.5 ;
this . x = options. x || w * 0.5 - this . bound. width * 0.5 ;
this . y = options. y || h * 0.5 - this . size * 0.5 ;
buffer. strokeStyle = this . color;
buffer. strokeText ( this . copy, 0 , this . bound. height * 0.8 ) ;
this . data = buffer. getImageData ( 0 , 0 , this . bound. width, this . bound. height) ;
this . index = 0 ;
this . update = function ( ) {
if ( this . index >= this . bound. width) {
this . index = 0 ;
return ;
}
const data = this . data. data;
for ( let i = this . index * 4 ; i < data. length; i += ( 4 * this . data. width) ) {
const bitmap = data[ i] + data[ i + 1 ] + data[ i + 2 ] + data[ i + 3 ] ;
if ( bitmap > 255 && Math. random ( ) > 0.96 ) {
const x = this . x + this . index;
const y = this . y + ( i / this . bound. width / 4 ) ;
thunder. push ( new Thunder ( {
x : x,
y : y
} ) ) ;
Math. random ( ) > 0.5 && particles. push ( new Particles ( {
x : x,
y : y
} ) ) ;
}
}
if ( this . delay-- < 0 ) {
this . index++ ;
this . delay += this . basedelay;
}
}
this . render = function ( ctx ) {
ctx. putImageData ( this . data, this . x, this . y, 0 , 0 , this . index, this . bound. height) ;
}
}
function loop ( ) {
update ( ) ;
render ( ) ;
requestAnimationFrame ( loop) ;
}
function update ( ) {
text. update ( ) ;
thunder. forEach ( ( l, i ) => l. update ( i, thunder) ) ;
particles. forEach ( p => p. update ( ) ) ;
}
function render ( ) {
ctx. globalCompositeOperation = 'source-over' ;
ctx. globalAlpha = 1 ;
ctx. fillStyle = '#000000' ;
ctx. fillRect ( 0 , 0 , w, h) ;
ctx. globalCompositeOperation = 'screen' ;
text. render ( ctx) ;
thunder. forEach ( l => l. render ( ctx) ) ;
particles. forEach ( p => p. render ( ctx) ) ;
}
( function ( ) {
canvas = document. getElementById ( 'canvas' ) ;
input = document. getElementById ( 'input' ) ;
ctx = canvas. getContext ( '2d' ) ;
w = window. innerWidth;
h = window. innerHeight;
canvas. width = w;
canvas. height = h;
thunder = [ ] ;
particles = [ ] ;
text = new Text ( {
copy : 'Anh Yêu Em!'
} ) ;
canvas. addEventListener ( 'click' , ( e ) => {
const x = e. clientX;
const y = e. clientY;
thunder. push ( new Thunder ( {
x : x,
y : y
} ) ) ;
particles. push ( new Particles ( {
x : x,
y : y
} ) ) ;
} ) ;
let cb = 0 ;
input. addEventListener ( 'keyup' , ( e ) => {
clearTimeout ( cb) ;
cb = setTimeout ( ( ) => {
text = new Text ( {
copy : input. value
} ) ;
} , 300 ) ;
} ) ;
loop ( ) ;
} ) ( )
</ script>
</ body>
</ html>