/* ----------------------------------------------- 搬运的大哥的 稍微写了些注释 想直接设置动画样式的翻到1565行附近 前面的基本不用看 请在css设置展示尺寸~~ /* ----------------------------------------------- */ var pjs = function(tag_id, params) { var canvas_el = document.queryselector('#' + tag_id + ' > .particles-js-canvas-el'); /* particles.js variables with default values */ this.pjs = { canvas: { el: canvas_el, w: canvas_el.offsetwidth, h: canvas_el.offsetheight }, particles: { number: { value: 400, density: { enable: true, value_area: 800 } }, color: { value: '#fff' }, shape: { type: 'circle', stroke: { width: 0, color: '#ff0000' }, polygon: { nb_sides: 5 }, image: { src: '', width: 100, height: 100 } }, opacity: { value: 1, random: false, anim: { enable: false, speed: 2, opacity_min: 0, sync: false } }, size: { value: 20, random: false, anim: { enable: false, speed: 20, size_min: 0, sync: false } }, line_linked: { enable: true, distance: 100, color: '#fff', opacity: 1, width: 1 }, move: { enable: true, speed: 2, direction: 'none', random: false, straight: false, out_mode: 'out', bounce: false, attract: { enable: false, rotatex: 3000, rotatey: 3000 } }, array: [] }, interactivity: { detect_on: 'canvas', events: { onhover: { enable: true, mode: 'grab' }, onclick: { enable: true, mode: 'push' }, resize: true }, modes: { grab: { distance: 100, line_linked: { opacity: 1 } }, bubble: { distance: 200, size: 80, duration: 0.7 }, repulse: { distance: 200, duration: 0.4 }, push: { particles_nb: 4 }, remove: { particles_nb: 2 } }, mouse: {} }, retina_detect: false, fn: { interact: {}, modes: {}, vendors: {} }, tmp: {} }; var pjs = this.pjs; /* params settings */ if (params) { object.deepextend(pjs, params); } pjs.tmp.obj = { size_value: pjs.particles.size.value, size_anim_speed: pjs.particles.size.anim.speed, move_speed: pjs.particles.move.speed, line_linked_distance: pjs.particles.line_linked.distance, line_linked_width: pjs.particles.line_linked.width, mode_grab_distance: pjs.interactivity.modes.grab.distance, mode_bubble_distance: pjs.interactivity.modes.bubble.distance, mode_bubble_size: pjs.interactivity.modes.bubble.size, mode_repulse_distance: pjs.interactivity.modes.repulse.distance }; pjs.fn.retinainit = function() { if (pjs.retina_detect && window.devicepixelratio > 1) { pjs.canvas.pxratio = window.devicepixelratio; pjs.tmp.retina = true; } else { pjs.canvas.pxratio = 1; pjs.tmp.retina = false; } pjs.canvas.w = pjs.canvas.el.offsetwidth * pjs.canvas.pxratio; pjs.canvas.h = pjs.canvas.el.offsetheight * pjs.canvas.pxratio; pjs.particles.size.value = pjs.tmp.obj.size_value * pjs.canvas.pxratio; pjs.particles.size.anim.speed = pjs.tmp.obj.size_anim_speed * pjs.canvas.pxratio; pjs.particles.move.speed = pjs.tmp.obj.move_speed * pjs.canvas.pxratio; pjs.particles.line_linked.distance = pjs.tmp.obj.line_linked_distance * pjs.canvas.pxratio; pjs.interactivity.modes.grab.distance = pjs.tmp.obj.mode_grab_distance * pjs.canvas.pxratio; pjs.interactivity.modes.bubble.distance = pjs.tmp.obj.mode_bubble_distance * pjs.canvas.pxratio; pjs.particles.line_linked.width = pjs.tmp.obj.line_linked_width * pjs.canvas.pxratio; pjs.interactivity.modes.bubble.size = pjs.tmp.obj.mode_bubble_size * pjs.canvas.pxratio; pjs.interactivity.modes.repulse.distance = pjs.tmp.obj.mode_repulse_distance * pjs.canvas.pxratio; }; /* ---------- pjs functions - canvas ------------ */ pjs.fn.canvasinit = function() { pjs.canvas.ctx = pjs.canvas.el.getcontext('2d'); }; pjs.fn.canvassize = function() { pjs.canvas.el.width = pjs.canvas.w; pjs.canvas.el.height = pjs.canvas.h; if (pjs && pjs.interactivity.events.resize) { window.addeventlistener('resize', function() { pjs.canvas.w = pjs.canvas.el.offsetwidth; pjs.canvas.h = pjs.canvas.el.offsetheight; /* resize canvas */ if (pjs.tmp.retina) { pjs.canvas.w *= pjs.canvas.pxratio; pjs.canvas.h *= pjs.canvas.pxratio; } pjs.canvas.el.width = pjs.canvas.w; pjs.canvas.el.height = pjs.canvas.h; /* repaint canvas on anim disabled */ if (!pjs.particles.move.enable) { pjs.fn.particlesempty(); pjs.fn.particlescreate(); pjs.fn.particlesdraw(); pjs.fn.vendors.densityautoparticles(); } /* density particles enabled */ pjs.fn.vendors.densityautoparticles(); }); } }; pjs.fn.canvaspaint = function() { pjs.canvas.ctx.fillrect(0, 0, pjs.canvas.w, pjs.canvas.h); }; pjs.fn.canvasclear = function() { pjs.canvas.ctx.clearrect(0, 0, pjs.canvas.w, pjs.canvas.h); }; /* --------- pjs functions - particles ----------- */ pjs.fn.particle = function(color, opacity, position) { /* size */ this.radius = (pjs.particles.size.random ? math.random() : 1) * pjs.particles.size.value; if (pjs.particles.size.anim.enable) { this.size_status = false; this.vs = pjs.particles.size.anim.speed / 100; if (!pjs.particles.size.anim.sync) { this.vs = this.vs * math.random(); } } /* position */ this.x = position ? position.x : math.random() * pjs.canvas.w; this.y = position ? position.y : math.random() * pjs.canvas.h; /* check position - into the canvas */ if (this.x > pjs.canvas.w - this.radius * 2) this.x = this.x - this.radius; else if (this.x < this.radius * 2) this.x = this.x + this.radius; if (this.y > pjs.canvas.h - this.radius * 2) this.y = this.y - this.radius; else if (this.y < this.radius * 2) this.y = this.y + this.radius; /* check position - avoid overlap */ if (pjs.particles.move.bounce) { pjs.fn.vendors.checkoverlap(this, position); } /* color */ this.color = {}; if (typeof(color.value) == 'object') { if (color.value instanceof array) { var color_selected = color.value[math.floor(math.random() * pjs.particles.color.value.length)]; this.color.rgb = hextorgb(color_selected); } else { if (color.value.r != undefined && color.value.g != undefined && color.value.b != undefined) { this.color.rgb = { r: color.value.r, g: color.value.g, b: color.value.b } } if (color.value.h != undefined && color.value.s != undefined && color.value.l != undefined) { this.color.hsl = { h: color.value.h, s: color.value.s, l: color.value.l } } } } else if (color.value == 'random') { this.color.rgb = { r: (math.floor(math.random() * (255 - 0 + 1)) + 0), g: (math.floor(math.random() * (255 - 0 + 1)) + 0), b: (math.floor(math.random() * (255 - 0 + 1)) + 0) } } else if (typeof(color.value) == 'string') { this.color = color; this.color.rgb = hextorgb(this.color.value); } /* opacity */ this.opacity = (pjs.particles.opacity.random ? math.random() : 1) * pjs.particles.opacity.value; if (pjs.particles.opacity.anim.enable) { this.opacity_status = false; this.vo = pjs.particles.opacity.anim.speed / 100; if (!pjs.particles.opacity.anim.sync) { this.vo = this.vo * math.random(); } } /* animation - velocity for speed */ var velbase = {} switch (pjs.particles.move.direction) { case 'top': velbase = { x: 0, y: -1 }; break; case 'top-right': velbase = { x: 0.5, y: -0.5 }; break; case 'right': velbase = { x: 1, y: -0 }; break; case 'bottom-right': velbase = { x: 0.5, y: 0.5 }; break; case 'bottom': velbase = { x: 0, y: 1 }; break; case 'bottom-left': velbase = { x: -0.5, y: 1 }; break; case 'left': velbase = { x: -1, y: 0 }; break; case 'top-left': velbase = { x: -0.5, y: -0.5 }; break; default: velbase = { x: 0, y: 0 }; break; } if (pjs.particles.move.straight) { this.vx = velbase.x; this.vy = velbase.y; if (pjs.particles.move.random) { this.vx = this.vx * (math.random()); this.vy = this.vy * (math.random()); } } else { this.vx = velbase.x + math.random() - 0.5; this.vy = velbase.y + math.random() - 0.5; } // var theta = 2.0 * math.pi * math.random(); // this.vx = math.cos(theta); // this.vy = math.sin(theta); this.vx_i = this.vx; this.vy_i = this.vy; /* if shape is image */ var shape_type = pjs.particles.shape.type; if (typeof(shape_type) == 'object') { if (shape_type instanceof array) { var shape_selected = shape_type[math.floor(math.random() * shape_type.length)]; this.shape = shape_selected; } } else { this.shape = shape_type; } if (this.shape == 'image') { var sh = pjs.particles.shape; this.img = { src: sh.image.src, ratio: sh.image.width / sh.image.height } if (!this.img.ratio) this.img.ratio = 1; if (pjs.tmp.img_type == 'svg' && pjs.tmp.source_svg != undefined) { pjs.fn.vendors.createsvgimg(this); if (pjs.tmp.pushing) { this.img.loaded = false; } } } }; pjs.fn.particle.prototype.draw = function() { var p = this; if (p.radius_bubble != undefined) { var radius = p.radius_bubble; } else { var radius = p.radius; } if (p.opacity_bubble != undefined) { var opacity = p.opacity_bubble; } else { var opacity = p.opacity; } if (p.color.rgb) { var color_value = 'rgba(' + p.color.rgb.r + ',' + p.color.rgb.g + ',' + p.color.rgb.b + ',' + opacity + ')'; } else { var color_value = 'hsla(' + p.color.hsl.h + ',' + p.color.hsl.s + '%,' + p.color.hsl.l + '%,' + opacity + ')'; } pjs.canvas.ctx.fillstyle = color_value; pjs.canvas.ctx.beginpath(); switch (p.shape) { case 'circle': pjs.canvas.ctx.arc(p.x, p.y, radius, 0, math.pi * 2, false); break; case 'edge': pjs.canvas.ctx.rect(p.x - radius, p.y - radius, radius * 2, radius * 2); break; case 'triangle': pjs.fn.vendors.drawshape(pjs.canvas.ctx, p.x - radius, p.y + radius / 1.66, radius * 2, 3, 2); break; case 'polygon': pjs.fn.vendors.drawshape( pjs.canvas.ctx, p.x - radius / (pjs.particles.shape.polygon.nb_sides / 3.5), // startx p.y - radius / (2.66 / 3.5), // starty radius * 2.66 / (pjs.particles.shape.polygon.nb_sides / 3), // sidelength pjs.particles.shape.polygon.nb_sides, // sidecountnumerator 1 // sidecountdenominator ); break; case 'star': pjs.fn.vendors.drawshape( pjs.canvas.ctx, p.x - radius * 2 / (pjs.particles.shape.polygon.nb_sides / 4), // startx p.y - radius / (2 * 2.66 / 3.5), // starty radius * 2 * 2.66 / (pjs.particles.shape.polygon.nb_sides / 3), // sidelength pjs.particles.shape.polygon.nb_sides, // sidecountnumerator 2 // sidecountdenominator ); break; case 'image': function draw() { pjs.canvas.ctx.drawimage( img_obj, p.x - radius, p.y - radius, radius * 2, radius * 2 / p.img.ratio ); } if (pjs.tmp.img_type == 'svg') { var img_obj = p.img.obj; } else { var img_obj = pjs.tmp.img_obj; } if (img_obj) { draw(); } break; } pjs.canvas.ctx.closepath(); if (pjs.particles.shape.stroke.width > 0) { pjs.canvas.ctx.strokestyle = pjs.particles.shape.stroke.color; pjs.canvas.ctx.linewidth = pjs.particles.shape.stroke.width; pjs.canvas.ctx.stroke(); } pjs.canvas.ctx.fill(); }; pjs.fn.particlescreate = function() { for (var i = 0; i < pjs.particles.number.value; i++) { pjs.particles.array.push(new pjs.fn.particle(pjs.particles.color, pjs.particles.opacity.value)); } }; pjs.fn.particlesupdate = function() { for (var i = 0; i < pjs.particles.array.length; i++) { /* the particle */ var p = pjs.particles.array[i]; // var d = ( dx = pjs.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pjs.interactivity.mouse.click_pos_y - p.y ) * dy; // var f = -bang_size / d; // if ( d < bang_size ) { // var t = math.atan2( dy, dx ); // p.vx = f * math.cos(t); // p.vy = f * math.sin(t); // } /* move the particle */ if (pjs.particles.move.enable) { var ms = pjs.particles.move.speed / 2; p.x += p.vx * ms; p.y += p.vy * ms; } /* change opacity status */ if (pjs.particles.opacity.anim.enable) { if (p.opacity_status == true) { if (p.opacity >= pjs.particles.opacity.value) p.opacity_status = false; p.opacity += p.vo; } else { if (p.opacity <= pjs.particles.opacity.anim.opacity_min) p.opacity_status = true; p.opacity -= p.vo; } if (p.opacity < 0) p.opacity = 0; } /* change size */ if (pjs.particles.size.anim.enable) { if (p.size_status == true) { if (p.radius >= pjs.particles.size.value) p.size_status = false; p.radius += p.vs; } else { if (p.radius <= pjs.particles.size.anim.size_min) p.size_status = true; p.radius -= p.vs; } if (p.radius < 0) p.radius = 0; } /* change particle position if it is out of canvas */ if (pjs.particles.move.out_mode == 'bounce') { var new_pos = { x_left: p.radius, x_right: pjs.canvas.w, y_top: p.radius, y_bottom: pjs.canvas.h } } else { var new_pos = { x_left: -p.radius, x_right: pjs.canvas.w + p.radius, y_top: -p.radius, y_bottom: pjs.canvas.h + p.radius } } if (p.x - p.radius > pjs.canvas.w) { p.x = new_pos.x_left; p.y = math.random() * pjs.canvas.h; } else if (p.x + p.radius < 0) { p.x = new_pos.x_right; p.y = math.random() * pjs.canvas.h; } if (p.y - p.radius > pjs.canvas.h) { p.y = new_pos.y_top; p.x = math.random() * pjs.canvas.w; } else if (p.y + p.radius < 0) { p.y = new_pos.y_bottom; p.x = math.random() * pjs.canvas.w; } /* out of canvas modes */ switch (pjs.particles.move.out_mode) { case 'bounce': if (p.x + p.radius > pjs.canvas.w) p.vx = -p.vx; else if (p.x - p.radius < 0) p.vx = -p.vx; if (p.y + p.radius > pjs.canvas.h) p.vy = -p.vy; else if (p.y - p.radius < 0) p.vy = -p.vy; break; } /* events */ if (isinarray('grab', pjs.interactivity.events.onhover.mode)) { pjs.fn.modes.grabparticle(p); } if (isinarray('bubble', pjs.interactivity.events.onhover.mode) || isinarray('bubble', pjs.interactivity.events.onclick.mode)) { pjs.fn.modes.bubbleparticle(p); } if (isinarray('repulse', pjs.interactivity.events.onhover.mode) || isinarray('repulse', pjs.interactivity.events.onclick.mode)) { pjs.fn.modes.repulseparticle(p); } /* interaction auto between particles */ if (pjs.particles.line_linked.enable || pjs.particles.move.attract.enable) { for (var j = i + 1; j < pjs.particles.array.length; j++) { var p2 = pjs.particles.array[j]; /* link particles */ if (pjs.particles.line_linked.enable) { pjs.fn.interact.linkparticles(p, p2); } /* attract particles */ if (pjs.particles.move.attract.enable) { pjs.fn.interact.attractparticles(p, p2); } /* bounce particles */ if (pjs.particles.move.bounce) { pjs.fn.interact.bounceparticles(p, p2); } } } } }; pjs.fn.particlesdraw = function() { /* clear canvas */ pjs.canvas.ctx.clearrect(0, 0, pjs.canvas.w, pjs.canvas.h); /* update each particles param */ pjs.fn.particlesupdate(); /* draw each particle */ for (var i = 0; i < pjs.particles.array.length; i++) { var p = pjs.particles.array[i]; p.draw(); } }; pjs.fn.particlesempty = function() { pjs.particles.array = []; }; pjs.fn.particlesrefresh = function() { /* init all */ cancelrequestanimframe(pjs.fn.checkanimframe); cancelrequestanimframe(pjs.fn.drawanimframe); pjs.tmp.source_svg = undefined; pjs.tmp.img_obj = undefined; pjs.tmp.count_svg = 0; pjs.fn.particlesempty(); pjs.fn.canvasclear(); /* restart */ pjs.fn.vendors.start(); }; /* ---------- pjs functions - particles interaction ------------ */ pjs.fn.interact.linkparticles = function(p1, p2) { var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = math.sqrt(dx * dx + dy * dy); /* draw a line between p1 and p2 if the distance between them is under the config distance */ if (dist <= pjs.particles.line_linked.distance) { var opacity_line = pjs.particles.line_linked.opacity - (dist / (1 / pjs.particles.line_linked.opacity)) / pjs.particles.line_linked.distance; if (opacity_line > 0) { /* style */ var color_line = pjs.particles.line_linked.color_rgb_line; pjs.canvas.ctx.strokestyle = 'rgba(' + color_line.r + ',' + color_line.g + ',' + color_line.b + ',' + opacity_line + ')'; pjs.canvas.ctx.linewidth = pjs.particles.line_linked.width; //pjs.canvas.ctx.linecap = 'round'; /* performance issue */ /* path */ pjs.canvas.ctx.beginpath(); pjs.canvas.ctx.moveto(p1.x, p1.y); pjs.canvas.ctx.lineto(p2.x, p2.y); pjs.canvas.ctx.stroke(); pjs.canvas.ctx.closepath(); } } }; pjs.fn.interact.attractparticles = function(p1, p2) { /* condensed particles */ var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = math.sqrt(dx * dx + dy * dy); if (dist <= pjs.particles.line_linked.distance) { var ax = dx / (pjs.particles.move.attract.rotatex * 1000), ay = dy / (pjs.particles.move.attract.rotatey * 1000); p1.vx -= ax; p1.vy -= ay; p2.vx += ax; p2.vy += ay; } } pjs.fn.interact.bounceparticles = function(p1, p2) { var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = math.sqrt(dx * dx + dy * dy), dist_p = p1.radius + p2.radius; if (dist <= dist_p) { p1.vx = -p1.vx; p1.vy = -p1.vy; p2.vx = -p2.vx; p2.vy = -p2.vy; } } /* ---------- pjs functions - modes events ------------ */ pjs.fn.modes.pushparticles = function(nb, pos) { pjs.tmp.pushing = true; for (var i = 0; i < nb; i++) { pjs.particles.array.push( new pjs.fn.particle( pjs.particles.color, pjs.particles.opacity.value, { 'x': pos ? pos.pos_x : math.random() * pjs.canvas.w, 'y': pos ? pos.pos_y : math.random() * pjs.canvas.h } ) ) if (i == nb - 1) { if (!pjs.particles.move.enable) { pjs.fn.particlesdraw(); } pjs.tmp.pushing = false; } } }; pjs.fn.modes.removeparticles = function(nb) { pjs.particles.array.splice(0, nb); if (!pjs.particles.move.enable) { pjs.fn.particlesdraw(); } }; pjs.fn.modes.bubbleparticle = function(p) { /* on hover event */ if (pjs.interactivity.events.onhover.enable && isinarray('bubble', pjs.interactivity.events.onhover.mode)) { var dx_mouse = p.x - pjs.interactivity.mouse.pos_x, dy_mouse = p.y - pjs.interactivity.mouse.pos_y, dist_mouse = math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse), ratio = 1 - dist_mouse / pjs.interactivity.modes.bubble.distance; function init() { p.opacity_bubble = p.opacity; p.radius_bubble = p.radius; } /* mousemove - check ratio */ if (dist_mouse <= pjs.interactivity.modes.bubble.distance) { if (ratio >= 0 && pjs.interactivity.status == 'mousemove') { /* size */ if (pjs.interactivity.modes.bubble.size != pjs.particles.size.value) { if (pjs.interactivity.modes.bubble.size > pjs.particles.size.value) { var size = p.radius + (pjs.interactivity.modes.bubble.size * ratio); if (size >= 0) { p.radius_bubble = size; } } else { var dif = p.radius - pjs.interactivity.modes.bubble.size, size = p.radius - (dif * ratio); if (size > 0) { p.radius_bubble = size; } else { p.radius_bubble = 0; } } } /* opacity */ if (pjs.interactivity.modes.bubble.opacity != pjs.particles.opacity.value) { if (pjs.interactivity.modes.bubble.opacity > pjs.particles.opacity.value) { var opacity = pjs.interactivity.modes.bubble.opacity * ratio; if (opacity > p.opacity && opacity <= pjs.interactivity.modes.bubble.opacity) { p.opacity_bubble = opacity; } } else { var opacity = p.opacity - (pjs.particles.opacity.value - pjs.interactivity.modes.bubble.opacity) * ratio; if (opacity < p.opacity && opacity >= pjs.interactivity.modes.bubble.opacity) { p.opacity_bubble = opacity; } } } } } else { init(); } /* mouseleave */ if (pjs.interactivity.status == 'mouseleave') { init(); } } /* on click event */ else if (pjs.interactivity.events.onclick.enable && isinarray('bubble', pjs.interactivity.events.onclick.mode)) { if (pjs.tmp.bubble_clicking) { var dx_mouse = p.x - pjs.interactivity.mouse.click_pos_x, dy_mouse = p.y - pjs.interactivity.mouse.click_pos_y, dist_mouse = math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse), time_spent = (new date().gettime() - pjs.interactivity.mouse.click_time) / 1000; if (time_spent > pjs.interactivity.modes.bubble.duration) { pjs.tmp.bubble_duration_end = true; } if (time_spent > pjs.interactivity.modes.bubble.duration * 2) { pjs.tmp.bubble_clicking = false; pjs.tmp.bubble_duration_end = false; } } function process(bubble_param, particles_param, p_obj_bubble, p_obj, id) { if (bubble_param != particles_param) { if (!pjs.tmp.bubble_duration_end) { if (dist_mouse <= pjs.interactivity.modes.bubble.distance) { if (p_obj_bubble != undefined) var obj = p_obj_bubble; else var obj = p_obj; if (obj != bubble_param) { var value = p_obj - (time_spent * (p_obj - bubble_param) / pjs.interactivity.modes.bubble.duration); if (id == 'size') p.radius_bubble = value; if (id == 'opacity') p.opacity_bubble = value; } } else { if (id == 'size') p.radius_bubble = undefined; if (id == 'opacity') p.opacity_bubble = undefined; } } else { if (p_obj_bubble != undefined) { var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pjs.interactivity.modes.bubble.duration), dif = bubble_param - value_tmp; value = bubble_param + dif; if (id == 'size') p.radius_bubble = value; if (id == 'opacity') p.opacity_bubble = value; } } } } if (pjs.tmp.bubble_clicking) { /* size */ process(pjs.interactivity.modes.bubble.size, pjs.particles.size.value, p.radius_bubble, p.radius, 'size'); /* opacity */ process(pjs.interactivity.modes.bubble.opacity, pjs.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity'); } } }; pjs.fn.modes.repulseparticle = function(p) { if (pjs.interactivity.events.onhover.enable && isinarray('repulse', pjs.interactivity.events.onhover.mode) && pjs.interactivity.status == 'mousemove') { var dx_mouse = p.x - pjs.interactivity.mouse.pos_x, dy_mouse = p.y - pjs.interactivity.mouse.pos_y, dist_mouse = math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse); var normvec = { x: dx_mouse / dist_mouse, y: dy_mouse / dist_mouse }, repulseradius = pjs.interactivity.modes.repulse.distance, velocity = 100, repulsefactor = clamp((1 / repulseradius) * (-1 * math.pow(dist_mouse / repulseradius, 2) + 1) * repulseradius * velocity, 0, 50); var pos = { x: p.x + normvec.x * repulsefactor, y: p.y + normvec.y * repulsefactor } if (pjs.particles.move.out_mode == 'bounce') { if (pos.x - p.radius > 0 && pos.x + p.radius < pjs.canvas.w) p.x = pos.x; if (pos.y - p.radius > 0 && pos.y + p.radius < pjs.canvas.h) p.y = pos.y; } else { p.x = pos.x; p.y = pos.y; } } else if (pjs.interactivity.events.onclick.enable && isinarray('repulse', pjs.interactivity.events.onclick.mode)) { if (!pjs.tmp.repulse_finish) { pjs.tmp.repulse_count++; if (pjs.tmp.repulse_count == pjs.particles.array.length) { pjs.tmp.repulse_finish = true; } } if (pjs.tmp.repulse_clicking) { var repulseradius = math.pow(pjs.interactivity.modes.repulse.distance / 6, 3); var dx = pjs.interactivity.mouse.click_pos_x - p.x, dy = pjs.interactivity.mouse.click_pos_y - p.y, d = dx * dx + dy * dy; var force = -repulseradius / d * 1; function process() { var f = math.atan2(dy, dx); p.vx = force * math.cos(f); p.vy = force * math.sin(f); if (pjs.particles.move.out_mode == 'bounce') { var pos = { x: p.x + p.vx, y: p.y + p.vy } if (pos.x + p.radius > pjs.canvas.w) p.vx = -p.vx; else if (pos.x - p.radius < 0) p.vx = -p.vx; if (pos.y + p.radius > pjs.canvas.h) p.vy = -p.vy; else if (pos.y - p.radius < 0) p.vy = -p.vy; } } // default if (d <= repulseradius) { process(); } // bang - slow motion mode // if(!pjs.tmp.repulse_finish){ // if(d <= repulseradius){ // process(); // } // }else{ // process(); // } } else { if (pjs.tmp.repulse_clicking == false) { p.vx = p.vx_i; p.vy = p.vy_i; } } } } pjs.fn.modes.grabparticle = function(p) { if (pjs.interactivity.events.onhover.enable && pjs.interactivity.status == 'mousemove') { var dx_mouse = p.x - pjs.interactivity.mouse.pos_x, dy_mouse = p.y - pjs.interactivity.mouse.pos_y, dist_mouse = math.sqrt(dx_mouse * dx_mouse + dy_mouse * dy_mouse); /* draw a line between the cursor and the particle if the distance between them is under the config distance */ if (dist_mouse <= pjs.interactivity.modes.grab.distance) { var opacity_line = pjs.interactivity.modes.grab.line_linked.opacity - (dist_mouse / (1 / pjs.interactivity.modes.grab.line_linked.opacity)) / pjs.interactivity.modes.grab.distance; if (opacity_line > 0) { /* style */ var color_line = pjs.particles.line_linked.color_rgb_line; pjs.canvas.ctx.strokestyle = 'rgba(' + color_line.r + ',' + color_line.g + ',' + color_line.b + ',' + opacity_line + ')'; pjs.canvas.ctx.linewidth = pjs.particles.line_linked.width; //pjs.canvas.ctx.linecap = 'round'; /* performance issue */ /* path */ pjs.canvas.ctx.beginpath(); pjs.canvas.ctx.moveto(p.x, p.y); pjs.canvas.ctx.lineto(pjs.interactivity.mouse.pos_x, pjs.interactivity.mouse.pos_y); pjs.canvas.ctx.stroke(); pjs.canvas.ctx.closepath(); } } } }; /* ---------- pjs functions - vendors ------------ */ pjs.fn.vendors.eventslisteners = function() { /* events target element */ if (pjs.interactivity.detect_on == 'window') { pjs.interactivity.el = window; } else { pjs.interactivity.el = pjs.canvas.el; } /* detect mouse pos - on hover / click event */ if (pjs.interactivity.events.onhover.enable || pjs.interactivity.events.onclick.enable) { /* el on mousemove */ pjs.interactivity.el.addeventlistener('mousemove', function(e) { if (pjs.interactivity.el == window) { var pos_x = e.clientx, pos_y = e.clienty; } else { var pos_x = e.offsetx || e.clientx, pos_y = e.offsety || e.clienty; } pjs.interactivity.mouse.pos_x = pos_x; pjs.interactivity.mouse.pos_y = pos_y; if (pjs.tmp.retina) { pjs.interactivity.mouse.pos_x *= pjs.canvas.pxratio; pjs.interactivity.mouse.pos_y *= pjs.canvas.pxratio; } pjs.interactivity.status = 'mousemove'; }); /* el on onmouseleave */ pjs.interactivity.el.addeventlistener('mouseleave', function(e) { pjs.interactivity.mouse.pos_x = null; pjs.interactivity.mouse.pos_y = null; pjs.interactivity.status = 'mouseleave'; }); } /* on click event */ if (pjs.interactivity.events.onclick.enable) { pjs.interactivity.el.addeventlistener('click', function() { pjs.interactivity.mouse.click_pos_x = pjs.interactivity.mouse.pos_x; pjs.interactivity.mouse.click_pos_y = pjs.interactivity.mouse.pos_y; pjs.interactivity.mouse.click_time = new date().gettime(); if (pjs.interactivity.events.onclick.enable) { switch (pjs.interactivity.events.onclick.mode) { case 'push': if (pjs.particles.move.enable) { pjs.fn.modes.pushparticles(pjs.interactivity.modes.push.particles_nb, pjs.interactivity.mouse); } else { if (pjs.interactivity.modes.push.particles_nb == 1) { pjs.fn.modes.pushparticles(pjs.interactivity.modes.push.particles_nb, pjs.interactivity.mouse); } else if (pjs.interactivity.modes.push.particles_nb > 1) { pjs.fn.modes.pushparticles(pjs.interactivity.modes.push.particles_nb); } } break; case 'remove': pjs.fn.modes.removeparticles(pjs.interactivity.modes.remove.particles_nb); break; case 'bubble': pjs.tmp.bubble_clicking = true; break; case 'repulse': pjs.tmp.repulse_clicking = true; pjs.tmp.repulse_count = 0; pjs.tmp.repulse_finish = false; settimeout(function() { pjs.tmp.repulse_clicking = false; }, pjs.interactivity.modes.repulse.duration * 1000) break; } } }); } }; pjs.fn.vendors.densityautoparticles = function() { if (pjs.particles.number.density.enable) { /* calc area */ var area = pjs.canvas.el.width * pjs.canvas.el.height / 1000; if (pjs.tmp.retina) { area = area / (pjs.canvas.pxratio * 2); } /* calc number of particles based on density area */ var nb_particles = area * pjs.particles.number.value / pjs.particles.number.density.value_area; /* add or remove x particles */ var missing_particles = pjs.particles.array.length - nb_particles; if (missing_particles < 0) pjs.fn.modes.pushparticles(math.abs(missing_particles)); else pjs.fn.modes.removeparticles(missing_particles); } }; pjs.fn.vendors.checkoverlap = function(p1, position) { for (var i = 0; i < pjs.particles.array.length; i++) { var p2 = pjs.particles.array[i]; var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = math.sqrt(dx * dx + dy * dy); if (dist <= p1.radius + p2.radius) { p1.x = position ? position.x : math.random() * pjs.canvas.w; p1.y = position ? position.y : math.random() * pjs.canvas.h; pjs.fn.vendors.checkoverlap(p1); } } }; pjs.fn.vendors.createsvgimg = function(p) { /* set color to svg element */ var svgxml = pjs.tmp.source_svg, rgbhex = /#([0-9a-f]{3,6})/gi, coloredsvgxml = svgxml.replace(rgbhex, function(m, r, g, b) { if (p.color.rgb) { var color_value = 'rgba(' + p.color.rgb.r + ',' + p.color.rgb.g + ',' + p.color.rgb.b + ',' + p.opacity + ')'; } else { var color_value = 'hsla(' + p.color.hsl.h + ',' + p.color.hsl.s + '%,' + p.color.hsl.l + '%,' + p.opacity + ')'; } return color_value; }); /* prepare to create img with colored svg */ var svg = new blob([coloredsvgxml], { type: 'image/svg+xml;charset=utf-8' }), domurl = window.url || window.webkiturl || window, url = domurl.createobjecturl(svg); /* create particle img obj */ var img = new image(); img.addeventlistener('load', function() { p.img.obj = img; p.img.loaded = true; domurl.revokeobjecturl(url); pjs.tmp.count_svg++; }); img.src = url; }; pjs.fn.vendors.destroypjs = function() { cancelanimationframe(pjs.fn.drawanimframe); canvas_el.remove(); pjsdom = null; }; pjs.fn.vendors.drawshape = function(c, startx, starty, sidelength, sidecountnumerator, sidecountdenominator) { // by programming thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/ var sidecount = sidecountnumerator * sidecountdenominator; var decimalsides = sidecountnumerator / sidecountdenominator; var interiorangledegrees = (180 * (decimalsides - 2)) / decimalsides; var interiorangle = math.pi - math.pi * interiorangledegrees / 180; // convert to radians c.save(); c.beginpath(); c.translate(startx, starty); c.moveto(0, 0); for (var i = 0; i < sidecount; i++) { c.lineto(sidelength, 0); c.translate(sidelength, 0); c.rotate(interiorangle); } //c.stroke(); c.fill(); c.restore(); }; pjs.fn.vendors.exportimg = function() { window.open(pjs.canvas.el.todataurl('image/png'), '_blank'); }; pjs.fn.vendors.loadimg = function(type) { pjs.tmp.img_error = undefined; if (pjs.particles.shape.image.src != '') { if (type == 'svg') { var xhr = new xmlhttprequest(); xhr.open('get', pjs.particles.shape.image.src); xhr.onreadystatechange = function(data) { if (xhr.readystate == 4) { if (xhr.status == 200) { pjs.tmp.source_svg = data.currenttarget.response; pjs.fn.vendors.checkbeforedraw(); } else { console.log('error pjs - image not found'); pjs.tmp.img_error = true; } } } xhr.send(); } else { var img = new image(); img.addeventlistener('load', function() { pjs.tmp.img_obj = img; pjs.fn.vendors.checkbeforedraw(); }); img.src = pjs.particles.shape.image.src; } } else { console.log('error pjs - no image.src'); pjs.tmp.img_error = true; } }; pjs.fn.vendors.draw = function() { if (pjs.particles.shape.type == 'image') { if (pjs.tmp.img_type == 'svg') { if (pjs.tmp.count_svg >= pjs.particles.number.value) { pjs.fn.particlesdraw(); if (!pjs.particles.move.enable) cancelrequestanimframe(pjs.fn.drawanimframe); else pjs.fn.drawanimframe = requestanimframe(pjs.fn.vendors.draw); } else { //console.log('still loading...'); if (!pjs.tmp.img_error) pjs.fn.drawanimframe = requestanimframe(pjs.fn.vendors.draw); } } else { if (pjs.tmp.img_obj != undefined) { pjs.fn.particlesdraw(); if (!pjs.particles.move.enable) cancelrequestanimframe(pjs.fn.drawanimframe); else pjs.fn.drawanimframe = requestanimframe(pjs.fn.vendors.draw); } else { if (!pjs.tmp.img_error) pjs.fn.drawanimframe = requestanimframe(pjs.fn.vendors.draw); } } } else { pjs.fn.particlesdraw(); if (!pjs.particles.move.enable) cancelrequestanimframe(pjs.fn.drawanimframe); else pjs.fn.drawanimframe = requestanimframe(pjs.fn.vendors.draw); } }; pjs.fn.vendors.checkbeforedraw = function() { // if shape is image if (pjs.particles.shape.type == 'image') { if (pjs.tmp.img_type == 'svg' && pjs.tmp.source_svg == undefined) { pjs.tmp.checkanimframe = requestanimframe(check); } else { //console.log('images loaded! cancel check'); cancelrequestanimframe(pjs.tmp.checkanimframe); if (!pjs.tmp.img_error) { pjs.fn.vendors.init(); pjs.fn.vendors.draw(); } } } else { pjs.fn.vendors.init(); pjs.fn.vendors.draw(); } }; pjs.fn.vendors.init = function() { /* init canvas + particles */ pjs.fn.retinainit(); pjs.fn.canvasinit(); pjs.fn.canvassize(); pjs.fn.canvaspaint(); pjs.fn.particlescreate(); pjs.fn.vendors.densityautoparticles(); /* particles.line_linked - convert hex colors to rgb */ pjs.particles.line_linked.color_rgb_line = hextorgb(pjs.particles.line_linked.color); }; pjs.fn.vendors.start = function() { if (isinarray('image', pjs.particles.shape.type)) { pjs.tmp.img_type = pjs.particles.shape.image.src.substr(pjs.particles.shape.image.src.length - 3); pjs.fn.vendors.loadimg(pjs.tmp.img_type); } else { pjs.fn.vendors.checkbeforedraw(); } }; /* ---------- pjs - start ------------ */ pjs.fn.vendors.eventslisteners(); pjs.fn.vendors.start(); }; /* ---------- global functions - vendors ------------ */ object.deepextend = function(destination, source) { for (var property in source) { if (source[property] && source[property].constructor && source[property].constructor === object) { destination[property] = destination[property] || {}; arguments.callee(destination[property], source[property]); } else { destination[property] = source[property]; } } return destination; }; window.requestanimframe = (function() { return window.requestanimationframe || window.webkitrequestanimationframe || window.mozrequestanimationframe || window.orequestanimationframe || window.msrequestanimationframe || function(callback) { window.settimeout(callback, 1000 / 60); }; })(); window.cancelrequestanimframe = (function() { return window.cancelanimationframe || window.webkitcancelrequestanimationframe || window.mozcancelrequestanimationframe || window.ocancelrequestanimationframe || window.mscancelrequestanimationframe || cleartimeout })(); function hextorgb(hex) { // by tim down - http://stackoverflow.com/a/5624139/3493650 // expand shorthand form (e.g. "03f") to full form (e.g. "0033ff") var shorthandregex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandregex, function(m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseint(result[1], 16), g: parseint(result[2], 16), b: parseint(result[3], 16) } : null; }; function clamp(number, min, max) { return math.min(math.max(number, min), max); }; function isinarray(value, array) { return array.indexof(value) > -1; } /* ---------- particles.js functions - start ------------ */ window.pjsdom = []; window.particlesjs = function(tag_id, params) { //console.log(params); /* no string id? so it's object params, and set the id with default id */ if (typeof(tag_id) != 'string') { params = tag_id; tag_id = 'particles-js'; } /* no id? set the id to default id */ if (!tag_id) { tag_id = 'particles-js'; } /* pjs elements */ var pjs_tag = document.getelementbyid(tag_id), pjs_canvas_class = 'particles-js-canvas-el', exist_canvas = pjs_tag.getelementsbyclassname(pjs_canvas_class); /* remove canvas if exists into the pjs target tag */ if (exist_canvas.length) { while (exist_canvas.length > 0) { pjs_tag.removechild(exist_canvas[0]); } } /* create canvas element */ var canvas_el = document.createelement('canvas'); canvas_el.classname = pjs_canvas_class; /* 动画相对元素的尺寸 */ canvas_el.style.width = "100%"; canvas_el.style.height = "100%"; /* append canvas */ var canvas = document.getelementbyid(tag_id).appendchild(canvas_el); /* launch particle.js */ if (canvas != null) { pjsdom.push(new pjs(tag_id, params)); } }; window.particlesjs.load = function(tag_id, path_config_json, callback) { /* load json config */ var xhr = new xmlhttprequest(); xhr.open('get', path_config_json); xhr.onreadystatechange = function(data) { if (xhr.readystate == 4) { if (xhr.status == 200) { var params = json.parse(data.currenttarget.response); window.particlesjs(tag_id, params); if (callback) callback(); } else { console.log('error pjs - xmlhttprequest status: ' + xhr.status); console.log('error pjs - file config not found'); } } }; xhr.send(); }; //这里可以设置端点、线 所有属性 哈哈哈 都放到这里了 particlesjs('particles-js', { "particles": { "number": { //下面这个数约大越密集 太大浏览器会卡 "value": 8, "density": { "enable": true, //下面这个数约大越稀疏 太小浏览器会卡 "value_area": 50 } }, //交点的颜色 "color": { "value": "#eaeaeb" }, //别动就对了-—— "shape": { "type": "circle", "stroke": { "width": 0, "color": "#ffffff" }, "polygon": { "nb_sides": 5 }, "image": { "src": "img/github.svg", "width": 100, "height": 100 } }, //透明度 "opacity": { "value": 0.8, "random": false, "anim": { "enable": false, "speed": 1, "opacity_min": 0.1, "sync": false } }, //端点尺寸 "size": { "value": 4, "random": true, "anim": { "enable": false, "speed": 40, "size_min": 0.1, "sync": false } }, //线 "line_linked": { "enable": true, "distance": 80, //距离内连线 "color": "#dbdbe5", "opacity": 0.8, "width": 1 }, //移动属性 "move": { "enable": true, //动画速度在这里!! "speed": 3, "direction": "none", "random": false, "straight": false, "out_mode": "out", "attract": { "enable": false, "rotatex": 600, "rotatey": 1200 } } }, //交互 "interactivity": { "detect_on": "canvas", "events": { //鼠标滑过 和 鼠标点击 属性模式 //true表示开启 false 关闭 "onhover": { "enable": true, "mode": "grab" }, "onclick": { "enable": true, "mode": "push" }, "resize": true }, //各种模式属性 把属性名替换 两个状态的 "mode": 即可1653/1657附近 "grab" "bubble" "repulse" "push" "remove" //下面是鼠标事件名称 "modes": { //鼠标与distance范围内点连线 "grab": { "distance": 100, "line_linked": { "opacity": 1 } }, //改变周围点相应属性 "bubble": { "distance": 100, "size": 10, "duration": 2, "opacity": 8, "speed": 3 }, //鼠标周围distance范围排斥 "repulse": { "distance": 100 }, //添加 或 删除点de数量 "push": { "particles_nb": 4 }, "remove": { "particles_nb": 2 } } }, "retina_detect": true, } );