585 lines
16 KiB
JavaScript
585 lines
16 KiB
JavaScript
/*
|
|
* Solo - A small and beautiful blogging system written in Java.
|
|
* Copyright (c) 2010-present, b3log.org
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
;(function ($) {
|
|
$.fn.circleMagic = function (options) {
|
|
|
|
var width, height, canvas, ctx, animateHeader = true;
|
|
var circles = [];
|
|
|
|
var settings = $.extend({
|
|
color: 'rgba(255,255,255,.5)',
|
|
radius: 10,
|
|
density: 0.3,
|
|
clearOffset: 0.2
|
|
}, options);
|
|
|
|
// Main
|
|
|
|
var container = this['0'];
|
|
initContainer();
|
|
addListeners();
|
|
|
|
function initContainer() {
|
|
width = container.offsetWidth;
|
|
height = container.offsetHeight;
|
|
|
|
// create canvas element
|
|
|
|
initCanvas();
|
|
canvas = document.getElementById('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
canvas.style.position = 'absolute';
|
|
canvas.style.left = '0';
|
|
canvas.style.bottom = '0';
|
|
ctx = canvas.getContext('2d');
|
|
|
|
// create circles
|
|
for (var x = 0; x < width * settings.density; x++) {
|
|
var c = new Circle();
|
|
circles.push(c);
|
|
}
|
|
animate();
|
|
}
|
|
|
|
//Init canvas element
|
|
function initCanvas() {
|
|
var canvasElement = document.createElement('canvas');
|
|
canvasElement.id = 'canvas';
|
|
container.appendChild(canvasElement);
|
|
canvasElement.parentElement.style.overflow = 'hidden';
|
|
|
|
}
|
|
|
|
// Event handling
|
|
function addListeners() {
|
|
window.addEventListener('resize', resize, false);
|
|
}
|
|
|
|
function resize() {
|
|
width = container.clientWidth;
|
|
height = container.clientHeight;
|
|
container.height = height + 'px';
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
}
|
|
|
|
function animate() {
|
|
if (animateHeader) {
|
|
ctx.clearRect(0, 0, width, height);
|
|
for (var i in circles) {
|
|
circles[i].draw();
|
|
}
|
|
}
|
|
requestAnimationFrame(animate);
|
|
}
|
|
|
|
function randomColor() {
|
|
var r = Math.floor(Math.random() * 255);
|
|
var g = Math.floor(Math.random() * 255);
|
|
var b = Math.floor(Math.random() * 255);
|
|
var alpha = Math.random().toPrecision(2);
|
|
return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
|
|
}
|
|
|
|
// Canvas manipulation
|
|
|
|
function Circle() {
|
|
var that = this;
|
|
|
|
// constructor
|
|
(function () {
|
|
that.pos = {};
|
|
init();
|
|
})();
|
|
|
|
function init() {
|
|
that.pos.x = Math.random() * width;
|
|
that.pos.y = height + Math.random() * 100;
|
|
that.alpha = 0.1 + Math.random() * settings.clearOffset;
|
|
that.scale = 0.1 + Math.random() * 0.3;
|
|
that.speed = Math.random();
|
|
if (settings.color === 'random') {
|
|
that.color = randomColor();
|
|
}
|
|
else {
|
|
that.color = settings.color;
|
|
}
|
|
}
|
|
|
|
this.draw = function () {
|
|
if (that.alpha <= 0) {
|
|
init();
|
|
}
|
|
that.pos.y -= that.speed;
|
|
that.alpha -= 0.0005;
|
|
ctx.beginPath();
|
|
ctx.arc(that.pos.x, that.pos.y, that.scale * settings.radius, 0, 2 * Math.PI, false);
|
|
ctx.fillStyle = that.color;
|
|
ctx.fill();
|
|
ctx.closePath();
|
|
};
|
|
}
|
|
}
|
|
})(jQuery);
|
|
|
|
/**
|
|
* Ribbons Class File.
|
|
* Creates low-poly ribbons background effect inside a target container.
|
|
*/
|
|
(function( name, factory )
|
|
{
|
|
if( typeof window === "object" )
|
|
{
|
|
window[ name ] = factory();
|
|
}
|
|
|
|
})( "Ribbons", function()
|
|
{
|
|
var _w = window,
|
|
_b = document.body,
|
|
_d = document.documentElement;
|
|
|
|
// random helper
|
|
var random = function()
|
|
{
|
|
if( arguments.length === 1 ) // only 1 argument
|
|
{
|
|
if( Array.isArray( arguments[0] ) ) // extract index from array
|
|
{
|
|
var index = Math.round( random( 0, arguments[0].length - 1 ) );
|
|
return arguments[0][ index ];
|
|
}
|
|
return random( 0, arguments[0] ); // assume numeric
|
|
}
|
|
else if( arguments.length === 2 ) // two arguments range
|
|
{
|
|
return Math.random() * ( arguments[1] - arguments[0] ) + arguments[0];
|
|
}
|
|
return 0; // default
|
|
};
|
|
|
|
// screen helper
|
|
var screenInfo = function( e )
|
|
{
|
|
var width = Math.max( 0, _w.innerWidth || _d.clientWidth || _b.clientWidth || 0 ),
|
|
height = Math.max( 0, _w.innerHeight || _d.clientHeight || _b.clientHeight || 0 ),
|
|
scrollx = Math.max( 0, _w.pageXOffset || _d.scrollLeft || _b.scrollLeft || 0 ) - ( _d.clientLeft || 0 ),
|
|
scrolly = Math.max( 0, _w.pageYOffset || _d.scrollTop || _b.scrollTop || 0 ) - ( _d.clientTop || 0 );
|
|
|
|
return {
|
|
width : width,
|
|
height : height,
|
|
ratio : width / height,
|
|
centerx : width / 2,
|
|
centery : height / 2,
|
|
scrollx : scrollx,
|
|
scrolly : scrolly,
|
|
};
|
|
};
|
|
|
|
// mouse/input helper
|
|
var mouseInfo = function( e )
|
|
{
|
|
var screen = screenInfo( e ),
|
|
mousex = e ? Math.max( 0, e.pageX || e.clientX || 0 ) : 0,
|
|
mousey = e ? Math.max( 0, e.pageY || e.clientY || 0 ) : 0;
|
|
|
|
return {
|
|
mousex : mousex,
|
|
mousey : mousey,
|
|
centerx : mousex - ( screen.width / 2 ),
|
|
centery : mousey - ( screen.height / 2 ),
|
|
};
|
|
};
|
|
|
|
// point object
|
|
var Point = function( x, y )
|
|
{
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.set( x, y );
|
|
};
|
|
Point.prototype = {
|
|
constructor: Point,
|
|
|
|
set: function( x, y )
|
|
{
|
|
this.x = ( x || 0 );
|
|
this.y = ( y || 0 );
|
|
},
|
|
copy: function( point )
|
|
{
|
|
this.x = ( point.x || 0 );
|
|
this.y = ( point.y || 0 );
|
|
return this;
|
|
},
|
|
multiply: function( x, y )
|
|
{
|
|
this.x *= ( x || 1 );
|
|
this.y *= ( y || 1 );
|
|
return this;
|
|
},
|
|
divide: function( x, y )
|
|
{
|
|
this.x /= ( x || 1 );
|
|
this.y /= ( y || 1 );
|
|
return this;
|
|
},
|
|
add: function( x, y )
|
|
{
|
|
this.x += ( x || 0 );
|
|
this.y += ( y || 0 );
|
|
return this;
|
|
},
|
|
subtract: function( x, y )
|
|
{
|
|
this.x -= ( x || 0 );
|
|
this.y -= ( y || 0 );
|
|
return this;
|
|
},
|
|
clampX: function( min, max )
|
|
{
|
|
this.x = Math.max( min, Math.min( this.x, max ) );
|
|
return this;
|
|
},
|
|
clampY: function( min, max )
|
|
{
|
|
this.y = Math.max( min, Math.min( this.y, max ) );
|
|
return this;
|
|
},
|
|
flipX: function()
|
|
{
|
|
this.x *= -1;
|
|
return this;
|
|
},
|
|
flipY: function()
|
|
{
|
|
this.y *= -1;
|
|
return this;
|
|
},
|
|
};
|
|
|
|
// class constructor
|
|
var Factory = function( options )
|
|
{
|
|
this._canvas = null;
|
|
this._context = null;
|
|
this._sto = null;
|
|
this._width = 0;
|
|
this._height = 0;
|
|
this._scroll = 0;
|
|
this._ribbons = [];
|
|
this._options = {
|
|
// ribbon color HSL saturation amount
|
|
colorSaturation: "80%",
|
|
// ribbon color HSL brightness amount
|
|
colorBrightness: "60%",
|
|
// ribbon color opacity amount
|
|
colorAlpha: 0.65,
|
|
// how fast to cycle through colors in the HSL color space
|
|
colorCycleSpeed : 6,
|
|
// where to start from on the Y axis on each side (top|min, middle|center, bottom|max, random)
|
|
verticalPosition : "center",
|
|
// how fast to get to the other side of the screen
|
|
horizontalSpeed : 150,
|
|
// how many ribbons to keep on screen at any given time
|
|
ribbonCount: 3,
|
|
// add stroke along with ribbon fill colors
|
|
strokeSize: 0,
|
|
// move ribbons vertically by a factor on page scroll
|
|
parallaxAmount : -0.5,
|
|
// add animation effect to each ribbon section over time
|
|
animateSections : true,
|
|
};
|
|
this._onDraw = this._onDraw.bind( this );
|
|
this._onResize = this._onResize.bind( this );
|
|
this._onScroll = this._onScroll.bind( this );
|
|
this.setOptions( options );
|
|
this.init();
|
|
};
|
|
|
|
// class prototype
|
|
Factory.prototype = {
|
|
constructor: Factory,
|
|
|
|
// Set and merge local options
|
|
setOptions: function( options )
|
|
{
|
|
if( typeof options === "object" )
|
|
{
|
|
for( var key in options )
|
|
{
|
|
if( options.hasOwnProperty( key ) )
|
|
{
|
|
this._options[ key ] = options[ key ];
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// Initialize the ribbons effect
|
|
init: function()
|
|
{
|
|
try
|
|
{
|
|
this._canvas = document.createElement( "canvas" );
|
|
this._canvas.style["display"] = "block";
|
|
this._canvas.style["position"] = "fixed";
|
|
this._canvas.style["margin"] = "0";
|
|
this._canvas.style["padding"] = "0";
|
|
this._canvas.style["border"] = "0";
|
|
this._canvas.style["outline"] = "0";
|
|
this._canvas.style["left"] = "0";
|
|
this._canvas.style["top"] = "0";
|
|
this._canvas.style["width"] = "100%";
|
|
this._canvas.style["height"] = "100%";
|
|
this._canvas.style["z-index"] = "-1";
|
|
this._onResize();
|
|
|
|
this._context = this._canvas.getContext( "2d" );
|
|
this._context.clearRect( 0, 0, this._width, this._height );
|
|
this._context.globalAlpha = this._options.colorAlpha;
|
|
|
|
window.addEventListener( "resize", this._onResize );
|
|
document.body.appendChild( this._canvas );
|
|
}
|
|
catch( e ) {
|
|
console.warn( "Canvas Context Error: " + e.toString() );
|
|
return;
|
|
}
|
|
this._onDraw();
|
|
},
|
|
|
|
// Create a new random ribbon and to the list
|
|
addRibbon: function()
|
|
{
|
|
// movement data
|
|
var dir = ( Math.round( random( 1, 9 ) ) > 5 ) ? "right" : "left",
|
|
stop = 1000,
|
|
hide = 200,
|
|
min = 0 - hide,
|
|
max = this._width + hide,
|
|
movex = 0,
|
|
movey = 0,
|
|
startx = ( dir === "right" ) ? min : max,
|
|
starty = Math.round( random( 0, this._height ) );
|
|
|
|
// asjust starty based on options
|
|
if( /^(top|min)$/i.test( this._options.verticalPosition ) )
|
|
{
|
|
starty = 0 + hide;
|
|
}
|
|
else if( /^(middle|center)$/i.test( this._options.verticalPosition ) )
|
|
{
|
|
starty = ( this._height / 2 );
|
|
}
|
|
else if( /^(bottom|max)$/i.test( this._options.verticalPosition ) )
|
|
{
|
|
starty = this._height - hide;
|
|
}
|
|
|
|
// ribbon sections data
|
|
var ribbon = [],
|
|
point1 = new Point( startx, starty ),
|
|
point2 = new Point( startx, starty ),
|
|
point3 = null,
|
|
color = Math.round( random( 0, 360 ) ),
|
|
delay = 0;
|
|
|
|
// buils ribbon sections
|
|
while( true )
|
|
{
|
|
if( stop <= 0 ) break; stop--;
|
|
|
|
movex = Math.round( ( Math.random() * 1 - 0.2 ) * this._options.horizontalSpeed );
|
|
movey = Math.round( ( Math.random() * 1 - 0.5 ) * ( this._height * 0.25 ) );
|
|
|
|
point3 = new Point();
|
|
point3.copy( point2 );
|
|
|
|
if( dir === "right" )
|
|
{
|
|
point3.add( movex, movey );
|
|
if( point2.x >= max ) break;
|
|
}
|
|
else if( dir === "left" )
|
|
{
|
|
point3.subtract( movex, movey );
|
|
if( point2.x <= min ) break;
|
|
}
|
|
// point3.clampY( 0, this._height );
|
|
|
|
ribbon.push({ // single ribbon section
|
|
point1 : new Point( point1.x, point1.y ),
|
|
point2 : new Point( point2.x, point2.y ),
|
|
point3 : point3,
|
|
color : color,
|
|
delay : delay,
|
|
dir : dir,
|
|
alpha : 0,
|
|
phase : 0,
|
|
});
|
|
|
|
point1.copy( point2 );
|
|
point2.copy( point3 );
|
|
|
|
delay += 4;
|
|
color += this._options.colorCycleSpeed;
|
|
}
|
|
this._ribbons.push( ribbon );
|
|
},
|
|
|
|
// Draw single section
|
|
_drawRibbonSection: function( section )
|
|
{
|
|
if( section )
|
|
{
|
|
if( section.phase >= 1 && section.alpha <= 0 )
|
|
{
|
|
return true; // done
|
|
}
|
|
if( section.delay <= 0 )
|
|
{
|
|
section.phase += 0.02;
|
|
section.alpha = Math.sin( section.phase ) * 1;
|
|
section.alpha = ( section.alpha <= 0 ) ? 0 : section.alpha;
|
|
section.alpha = ( section.alpha >= 1 ) ? 1 : section.alpha;
|
|
|
|
if( this._options.animateSections )
|
|
{
|
|
var mod = ( Math.sin( 1 + section.phase * Math.PI / 2 ) * 0.1 );
|
|
|
|
if( section.dir === "right" )
|
|
{
|
|
section.point1.add( mod, 0 );
|
|
section.point2.add( mod, 0 );
|
|
section.point3.add( mod, 0 );
|
|
} else {
|
|
section.point1.subtract( mod, 0 );
|
|
section.point2.subtract( mod, 0 );
|
|
section.point3.subtract( mod, 0 );
|
|
}
|
|
section.point1.add( 0, mod );
|
|
section.point2.add( 0, mod );
|
|
section.point3.add( 0, mod );
|
|
}
|
|
}
|
|
else { section.delay -= 0.5; }
|
|
|
|
var s = this._options.colorSaturation,
|
|
l = this._options.colorBrightness,
|
|
c = "hsla("+ section.color +", "+ s +", "+ l +", "+ section.alpha +" )";
|
|
|
|
this._context.save();
|
|
|
|
if( this._options.parallaxAmount !== 0 )
|
|
{
|
|
this._context.translate( 0, this._scroll * this._options.parallaxAmount );
|
|
}
|
|
this._context.beginPath();
|
|
this._context.moveTo( section.point1.x, section.point1.y );
|
|
this._context.lineTo( section.point2.x, section.point2.y );
|
|
this._context.lineTo( section.point3.x, section.point3.y );
|
|
this._context.fillStyle = c;
|
|
this._context.fill();
|
|
|
|
if( this._options.strokeSize > 0 )
|
|
{
|
|
this._context.lineWidth = this._options.strokeSize;
|
|
this._context.strokeStyle = c;
|
|
this._context.lineCap = "round";
|
|
this._context.stroke();
|
|
}
|
|
this._context.restore();
|
|
}
|
|
return false; // not done yet
|
|
},
|
|
|
|
// Draw ribbons
|
|
_onDraw: function()
|
|
{
|
|
// cleanup on ribbons list to rtemoved finished ribbons
|
|
for( var i = 0, t = this._ribbons.length; i < t; ++i )
|
|
{
|
|
if( !this._ribbons[ i ] )
|
|
{
|
|
this._ribbons.splice( i, 1 );
|
|
}
|
|
}
|
|
|
|
// draw new ribbons
|
|
this._context.clearRect( 0, 0, this._width, this._height );
|
|
|
|
for( var a = 0; a < this._ribbons.length; ++a ) // single ribbon
|
|
{
|
|
var ribbon = this._ribbons[ a ],
|
|
numSections = ribbon.length,
|
|
numDone = 0;
|
|
|
|
for( var b = 0; b < numSections; ++b ) // ribbon section
|
|
{
|
|
if( this._drawRibbonSection( ribbon[ b ] ) )
|
|
{
|
|
numDone++; // section done
|
|
}
|
|
}
|
|
if( numDone >= numSections ) // ribbon done
|
|
{
|
|
this._ribbons[ a ] = null;
|
|
}
|
|
}
|
|
// maintain optional number of ribbons on canvas
|
|
if( this._ribbons.length < this._options.ribbonCount )
|
|
{
|
|
this.addRibbon();
|
|
}
|
|
requestAnimationFrame( this._onDraw );
|
|
},
|
|
|
|
// Update container size info
|
|
_onResize: function( e )
|
|
{
|
|
var screen = screenInfo( e );
|
|
this._width = screen.width;
|
|
this._height = screen.height;
|
|
|
|
if( this._canvas )
|
|
{
|
|
this._canvas.width = this._width;
|
|
this._canvas.height = this._height;
|
|
|
|
if( this._context )
|
|
{
|
|
this._context.globalAlpha = this._options.colorAlpha;
|
|
}
|
|
}
|
|
},
|
|
|
|
// Update container size info
|
|
_onScroll: function( e )
|
|
{
|
|
var screen = screenInfo( e );
|
|
this._scroll = screen.scrolly;
|
|
},
|
|
|
|
};
|
|
|
|
// export
|
|
return Factory;
|
|
}); |