Merge branch 'feature-particleground'

This commit is contained in:
pixeldesu 2015-05-13 16:18:47 +02:00
commit 0fa564a137
6 changed files with 525 additions and 20 deletions

View file

@ -8,6 +8,7 @@
#= require growl #= require growl
#= require cheet #= require cheet
#= require jquery.guillotine #= require jquery.guillotine
#= require jquery.particleground
#= require sweet-alert #= require sweet-alert
# local requires to be seen by everyone: # local requires to be seen by everyone:
#= require_tree ./answerbox #= require_tree ./answerbox
@ -44,5 +45,9 @@ _ready = ->
if typeof sweetAlertInitialize != "undefined" if typeof sweetAlertInitialize != "undefined"
sweetAlertInitialize() sweetAlertInitialize()
particleground document.getElementById('particles'),
dotColor: '#5e35b1'
lineColor: '#5e35b1'
$(document).ready _ready $(document).ready _ready
$(document).on 'page:load', _ready $(document).on 'page:load', _ready

View file

@ -157,3 +157,35 @@ body {
border-bottom-left-radius: 2px; border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px; border-bottom-right-radius: 2px;
} }
.particle-jumbotron {
padding: 0px;
overflow: hidden;
position: relative;
width: 100%;
}
.particle-content {
position: relative;
top: 0;
padding-top: 48px;
padding-bottom: 48px;
padding-left: 30px;
padding-right: 30px;
}
#particles {
position: absolute;
width: 100%;
height: 100%;
}
.icon-showcase {
font-size: 78px;
text-align: center;
display: block;
}
.heading-showcase {
margin-top: 5px;
}

View file

@ -1,31 +1,38 @@
.jumbotron.j2-jumbo.text-center .jumbotron.j2-jumbo.text-center.particle-jumbotron
.container #particles
= render 'layouts/messages' .particle-content
%h1= APP_CONFIG['site_name'] .container
%p Ask questions, give answers and learn more about your friends. = render 'layouts/messages'
%p %h1= APP_CONFIG['site_name']
%a.btn.btn-primary.btn-lg{href: url_for(new_user_registration_path)} %p Ask questions, give answers and learn more about your friends.
Register now %p
%small %a.btn.btn-primary.btn-lg{href: url_for(new_user_registration_path)}
Already a member? Register now
= link_to 'Sign in', new_user_session_path %small
Already a member?
= link_to 'Sign in', new_user_session_path
.container-fluid .container-fluid
%h2.text-center Features
.row.text-center .row.text-center
.col-md-4.col-sm-4.col-xs-12 .col-md-4.col-sm-4.col-xs-12
%h3 .icon-showcase
%i.fa.fa-comments
%h3.heading-showcase
Ask and answer questions Ask and answer questions
%p %p
With With
= APP_CONFIG['site_name'] = APP_CONFIG['site_name']
you can ask people questions and answer questions from other users or unregistered people. Want to know something more? Keep the discussion ongoing in the comments! you can ask people questions and answer questions from other users or unregistered people. Want to know something more? Keep the discussion ongoing in the comments!
.col-md-4.col-sm-4.col-xs-12 .col-md-4.col-sm-4.col-xs-12
%h3 .icon-showcase
%i.fa.fa-users
%h3.heading-showcase
Follow users and get followed Follow users and get followed
%p %p
Following users allows you to get a personalized feed of all people you want to know more about. You can also send a question to all your followers at once! Following users allows you to get a personalized feed of all people you want to know more about. You can also send a question to all your followers at once!
.col-md-4.col-sm-4.col-xs-12 .col-md-4.col-sm-4.col-xs-12
%h3 .icon-showcase
%i.fa.fa-share-square-o
%h3.heading-showcase
Sharing to other networks Sharing to other networks
%p %p
Want to share your answer to a question so that more people read it? With a simple click on the answer button, your answer is shared wherever you want! Want to share your answer to a question so that more people read it? With a simple click on the answer button, your answer is shared wherever you want!

View file

@ -1,7 +1,9 @@
- provide(:title, "About | #{APP_CONFIG['site_name']}") - provide(:title, "About | #{APP_CONFIG['site_name']}")
.jumbotron.j2-jumbo.text-center .jumbotron.j2-jumbo.text-center.particle-jumbotron
%h1= APP_CONFIG['site_name'] #particles
%p About our service, features and other information .particle-content
%h1= APP_CONFIG['site_name']
%p About our service, features and other information
.container .container
= render 'layouts/messages' = render 'layouts/messages'

View file

@ -1,7 +1,13 @@
- provide(:title, "Frequently Asked Questions | #{APP_CONFIG['site_name']}") - provide(:title, "Frequently Asked Questions | #{APP_CONFIG['site_name']}")
.jumbotron.j2-jumbo.text-center.particle-jumbotron
#particles
.particle-content
%h1 Frequently Asked Questions
%p
Everything you want to know about
= succeed '!' do
= APP_CONFIG['site_name']
.container .container
%h1.text-center Frequently Asked Questions
.panel-group{id: "accordion", role: "tablist", aria: {multiselectable: :true}} .panel-group{id: "accordion", role: "tablist", aria: {multiselectable: :true}}
.panel.panel-default .panel.panel-default
.panel-heading{id: "faqOne", role: "tab"} .panel-heading{id: "faqOne", role: "tab"}

View file

@ -0,0 +1,453 @@
/*!
* Particleground
*
* @author Jonathan Nicol - @mrjnicol
* @version 1.1.0
* @description Creates a canvas based particle system background
*
* Inspired by http://requestlab.fr/ and http://disruptivebydesign.com/
*/
;(function(window, document) {
"use strict";
var pluginName = 'particleground';
// http://youmightnotneedjquery.com/#deep_extend
function extend(out) {
out = out || {};
for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i];
if (!obj) continue;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object')
deepExtend(out[key], obj[key]);
else
out[key] = obj[key];
}
}
}
return out;
};
var $ = window.jQuery;
function Plugin(element, options) {
var canvasSupport = !!document.createElement('canvas').getContext;
var canvas;
var ctx;
var particles = [];
var raf;
var mouseX = 0;
var mouseY = 0;
var winW;
var winH;
var desktop = !navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|BB10|mobi|tablet|opera mini|nexus 7)/i);
var orientationSupport = !!window.DeviceOrientationEvent;
var tiltX = 0;
var pointerX;
var pointerY;
var tiltY = 0;
var paused = false;
options = extend({}, window[pluginName].defaults, options);
/**
* Init
*/
function init() {
if (!canvasSupport) { return; }
//Create canvas
canvas = document.createElement('canvas');
canvas.className = 'pg-canvas';
canvas.style.display = 'block';
element.insertBefore(canvas, element.firstChild);
ctx = canvas.getContext('2d');
styleCanvas();
// Create particles
var numParticles = Math.round((canvas.width * canvas.height) / options.density);
for (var i = 0; i < numParticles; i++) {
var p = new Particle();
p.setStackPos(i);
particles.push(p);
};
window.addEventListener('resize', function() {
resizeHandler();
}, false);
document.addEventListener('mousemove', function(e) {
mouseX = e.pageX;
mouseY = e.pageY;
}, false);
if (orientationSupport && !desktop) {
window.addEventListener('deviceorientation', function () {
// Contrain tilt range to [-30,30]
tiltY = Math.min(Math.max(-event.beta, -30), 30);
tiltX = Math.min(Math.max(-event.gamma, -30), 30);
}, true);
}
draw();
hook('onInit');
}
/**
* Style the canvas
*/
function styleCanvas() {
canvas.width = element.offsetWidth;
canvas.height = element.offsetHeight;
ctx.fillStyle = options.dotColor;
ctx.strokeStyle = options.lineColor;
ctx.lineWidth = options.lineWidth;
}
/**
* Draw particles
*/
function draw() {
if (!canvasSupport) { return; }
winW = window.innerWidth;
winH = window.innerHeight;
// Wipe canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update particle positions
for (var i = 0; i < particles.length; i++) {
particles[i].updatePosition();
};
// Draw particles
for (var i = 0; i < particles.length; i++) {
particles[i].draw();
};
// Call this function next time screen is redrawn
if (!paused) {
raf = requestAnimationFrame(draw);
}
}
/**
* Add/remove particles.
*/
function resizeHandler() {
// Resize the canvas
styleCanvas();
var elWidth = element.offsetWidth;
var elHeight = element.offsetHeight;
// Remove particles that are outside the canvas
for (var i = particles.length - 1; i >= 0; i--) {
if (particles[i].position.x > elWidth || particles[i].position.y > elHeight) {
particles.splice(i, 1);
}
};
// Adjust particle density
var numParticles = Math.round((canvas.width * canvas.height) / options.density);
if (numParticles > particles.length) {
while (numParticles > particles.length) {
var p = new Particle();
particles.push(p);
}
} else if (numParticles < particles.length) {
particles.splice(numParticles);
}
// Re-index particles
for (i = particles.length - 1; i >= 0; i--) {
particles[i].setStackPos(i);
};
}
/**
* Pause particle system
*/
function pause() {
paused = true;
}
/**
* Start particle system
*/
function start() {
paused = false;
draw();
}
/**
* Particle
*/
function Particle() {
this.stackPos;
this.active = true;
this.layer = Math.ceil(Math.random() * 3);
this.parallaxOffsetX = 0;
this.parallaxOffsetY = 0;
// Initial particle position
this.position = {
x: Math.ceil(Math.random() * canvas.width),
y: Math.ceil(Math.random() * canvas.height)
}
// Random particle speed, within min and max values
this.speed = {}
switch (options.directionX) {
case 'left':
this.speed.x = +(-options.maxSpeedX + (Math.random() * options.maxSpeedX) - options.minSpeedX).toFixed(2);
break;
case 'right':
this.speed.x = +((Math.random() * options.maxSpeedX) + options.minSpeedX).toFixed(2);
break;
default:
this.speed.x = +((-options.maxSpeedX / 2) + (Math.random() * options.maxSpeedX)).toFixed(2);
this.speed.x += this.speed.x > 0 ? options.minSpeedX : -options.minSpeedX;
break;
}
switch (options.directionY) {
case 'up':
this.speed.y = +(-options.maxSpeedY + (Math.random() * options.maxSpeedY) - options.minSpeedY).toFixed(2);
break;
case 'down':
this.speed.y = +((Math.random() * options.maxSpeedY) + options.minSpeedY).toFixed(2);
break;
default:
this.speed.y = +((-options.maxSpeedY / 2) + (Math.random() * options.maxSpeedY)).toFixed(2);
this.speed.x += this.speed.y > 0 ? options.minSpeedY : -options.minSpeedY;
break;
}
}
/**
* Draw particle
*/
Particle.prototype.draw = function() {
// Draw circle
ctx.beginPath();
ctx.arc(this.position.x + this.parallaxOffsetX, this.position.y + this.parallaxOffsetY, options.particleRadius / 2, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
// Draw lines
ctx.beginPath();
// Iterate over all particles which are higher in the stack than this one
for (var i = particles.length - 1; i > this.stackPos; i--) {
var p2 = particles[i];
// Pythagorus theorum to get distance between two points
var a = this.position.x - p2.position.x
var b = this.position.y - p2.position.y
var dist = Math.sqrt((a * a) + (b * b)).toFixed(2);
// If the two particles are in proximity, join them
if (dist < options.proximity) {
ctx.moveTo(this.position.x + this.parallaxOffsetX, this.position.y + this.parallaxOffsetY);
if (options.curvedLines) {
ctx.quadraticCurveTo(Math.max(p2.position.x, p2.position.x), Math.min(p2.position.y, p2.position.y), p2.position.x + p2.parallaxOffsetX, p2.position.y + p2.parallaxOffsetY);
} else {
ctx.lineTo(p2.position.x + p2.parallaxOffsetX, p2.position.y + p2.parallaxOffsetY);
}
}
}
ctx.stroke();
ctx.closePath();
}
/**
* update particle position
*/
Particle.prototype.updatePosition = function() {
if (options.parallax) {
if (orientationSupport && !desktop) {
// Map tiltX range [-30,30] to range [0,winW]
var ratioX = (winW - 0) / (30 - -30);
pointerX = (tiltX - -30) * ratioX + 0;
// Map tiltY range [-30,30] to range [0,winH]
var ratioY = (winH - 0) / (30 - -30);
pointerY = (tiltY - -30) * ratioY + 0;
} else {
pointerX = mouseX;
pointerY = mouseY;
}
// Calculate parallax offsets
this.parallaxTargX = (pointerX - (winW / 2)) / (options.parallaxMultiplier * this.layer);
this.parallaxOffsetX += (this.parallaxTargX - this.parallaxOffsetX) / 10; // Easing equation
this.parallaxTargY = (pointerY - (winH / 2)) / (options.parallaxMultiplier * this.layer);
this.parallaxOffsetY += (this.parallaxTargY - this.parallaxOffsetY) / 10; // Easing equation
}
var elWidth = element.offsetWidth;
var elHeight = element.offsetHeight;
switch (options.directionX) {
case 'left':
if (this.position.x + this.speed.x + this.parallaxOffsetX < 0) {
this.position.x = elWidth - this.parallaxOffsetX;
}
break;
case 'right':
if (this.position.x + this.speed.x + this.parallaxOffsetX > elWidth) {
this.position.x = 0 - this.parallaxOffsetX;
}
break;
default:
// If particle has reached edge of canvas, reverse its direction
if (this.position.x + this.speed.x + this.parallaxOffsetX > elWidth || this.position.x + this.speed.x + this.parallaxOffsetX < 0) {
this.speed.x = -this.speed.x;
}
break;
}
switch (options.directionY) {
case 'up':
if (this.position.y + this.speed.y + this.parallaxOffsetY < 0) {
this.position.y = elHeight - this.parallaxOffsetY;
}
break;
case 'down':
if (this.position.y + this.speed.y + this.parallaxOffsetY > elHeight) {
this.position.y = 0 - this.parallaxOffsetY;
}
break;
default:
// If particle has reached edge of canvas, reverse its direction
if (this.position.y + this.speed.y + this.parallaxOffsetY > elHeight || this.position.y + this.speed.y + this.parallaxOffsetY < 0) {
this.speed.y = -this.speed.y;
}
break;
}
// Move particle
this.position.x += this.speed.x;
this.position.y += this.speed.y;
}
/**
* Setter: particle stacking position
*/
Particle.prototype.setStackPos = function(i) {
this.stackPos = i;
}
function option (key, val) {
if (val) {
options[key] = val;
} else {
return options[key];
}
}
function destroy() {
console.log('destroy');
canvas.parentNode.removeChild(canvas);
hook('onDestroy');
if ($) {
$(element).removeData('plugin_' + pluginName);
}
}
function hook(hookName) {
if (options[hookName] !== undefined) {
options[hookName].call(element);
}
}
init();
return {
option: option,
destroy: destroy,
start: start,
pause: pause
};
}
window[pluginName] = function(elem, options) {
return new Plugin(elem, options);
};
window[pluginName].defaults = {
minSpeedX: 0.1,
maxSpeedX: 0.7,
minSpeedY: 0.1,
maxSpeedY: 0.7,
directionX: 'center', // 'center', 'left' or 'right'. 'center' = dots bounce off edges
directionY: 'center', // 'center', 'up' or 'down'. 'center' = dots bounce off edges
density: 10000, // How many particles will be generated: one particle every n pixels
dotColor: '#666666',
lineColor: '#666666',
particleRadius: 7, // Dot size
lineWidth: 1,
curvedLines: false,
proximity: 100, // How close two dots need to be before they join
parallax: true,
parallaxMultiplier: 5, // The lower the number, the more extreme the parallax effect
onInit: function() {},
onDestroy: function() {}
};
// nothing wrong with hooking into jQuery if it's there...
if ($) {
$.fn[pluginName] = function(options) {
if (typeof arguments[0] === 'string') {
var methodName = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
var returnVal;
this.each(function() {
if ($.data(this, 'plugin_' + pluginName) && typeof $.data(this, 'plugin_' + pluginName)[methodName] === 'function') {
returnVal = $.data(this, 'plugin_' + pluginName)[methodName].apply(this, args);
}
});
if (returnVal !== undefined){
return returnVal;
} else {
return this;
}
} else if (typeof options === "object" || !options) {
return this.each(function() {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
}
});
}
};
}
})(window, document);
/**
* requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
* @see: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
* @see: http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
* @license: MIT license
*/
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());