说明:

本文章主要分为ES5和ES6两个版本

ES5版本是早期版本,后面用ES6重写优化的,建议使用ES6版本。

1, 原生js实现canvas气泡冒泡效果的插件,api丰富,使用简单
2, 只需引入JumpBubble.js一个js文件即可

项目源码地址: https://github.com/roonly/JumpBubble

== 使用demo:

ES6版本的使用demo:

const bubble = new JumpBubble(document.getElementById(`cavs`));

bubble.create('/img/fish.png');

ES5代码的时候demo:

var demo = new JumpBubble({
  elCanvas : document.getElementById("canvasIdName")
});
demo.create({
  elImg : document.getElementById("imgIdName")
});

== 效果:

== html 代码:

ES6版本:

//html内容
<canvas id="cavs" width="130" height="400" style="border:1px solid #fff;">您的浏览器不支持canvas标签~</canvas> //index.js内容:
let list = [
'http://p4.cdn.btime.com/t01e430315c854b44d2.png',
'http://p5.qhimg.com/t017f9904d4be818a87.png',
'http://p5.qhimg.com/t015ec16e404a442dd4.png',
'/img/fish.png', //注:路径是相对html的路径,因为该路径最终会放到img标签的src上
];
const bubble = new JumpBubble(document.getElementById(`cavs`));
setInterval(() => {
if(s > list.length - 1){
s = 0;
}
bubble.create(list[s]);
s++
},250);

ES5版本:

<!DOCTYPE HTML>
<html>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>原生js实现canvas气泡冒泡效果</title>
<body>
<p style="display:none">
<img id="img1" src="http://p8.qhimg.com/t01053ab4d4d6510abd.png" alt="">
<img id="img2" src="http://p5.qhimg.com/t017f9904d4be818a87.png" alt="">
<img id="img3" src="http://p5.qhimg.com/t015ec16e404a442dd4.png" alt="">
<img id="img4" src="http://p6.qhimg.com/t017895dcd6312beacb.png" alt="">
<img id="img5" src="http://p2.qhimg.com/t01f70bccf10e16addd.png" alt="">
<img id="img6" src="http://p3.qhimg.com/t016d419cab67d819ac.png" alt="">
</p>
<h2>小贼说:原生js实现canvas气泡冒泡效果的插件,api丰富,使用简单</h2>
<canvas id="myCanvas" width="250" height="430" style="border:1px solid #eee">您的浏览器不支持canvas标签~</canvas>
<canvas id="myCanvas2" width="200" height="330" style="border:1px solid #eee">您的浏览器不支持canvas标签~</canvas>
<script src="JumpBubble.js"></script>
<script type="text/javascript">
window.onload = function(){
// 使用demo
var demo = new JumpBubble({
elCanvas : document.getElementById("myCanvas")
});
clearInterval(setDemo1);
var setDemo1 = setInterval(function(){
var idName = "img" + Math.ceil(Math.random()*6);
demo.create({
elImg : document.getElementById(idName)
});
},150); // demo2
var demo2 = new JumpBubble({
elCanvas : document.getElementById("myCanvas2"),
config : {
alpha : 0.5,
width : 30
},
callback : function(a,b,c){}
});
clearInterval(setDemo2);
var setDemo2 = setInterval(function(){
demo2.create({
elImg : document.getElementById("img1")
});
},150); } </script>
</body>
</html>

== javascript 代码:(分为ES6版 和 ES5版)

ES6版:

 /*
** @param canvasNode [必传] <DOM> canvan标签元素
** @param config [选传] <Obj> 可选配置项
** - effect <Str> 气泡的y轴浮动效果,可选2个值:linear、ease,默认'ease'
** - speed <Str> 气泡的Y轴移动速度,可选3个值 slow default fast, 默认'default'
** - isToAlapha <Boo> 气泡是否逐渐增加透明度 默认true
** - alpha <Num> 初始气泡的透明度, 默认0.9
** - max <Num> //画布上最多同时存在多少气泡,默认30,请根据气泡出现频道调整此值,已达到最优的视觉效果
** - diffWidth <Num> 初始冒泡时气泡宽度与指定宽度(width属性值)的差值,默认15,差值越大,冒泡时气泡从小变大的效果越明显
** - left <Num> 冒泡位置,距离画布左侧像素数,默认在画布中间靠左15像素位置
** - top <Num> 冒泡位置,距离画布顶部像素数,默认距离画布顶部为画布高度减30像素
** - width <Num> 气泡的宽度, 默认30,为保证气泡不变形,高度随宽度改变
** @param callback [选传] <Func> 态度气泡实例初始化后的回调
*/
export default class JumpBubble{
constructor(canvasNode, config = {}, callback ){
if(!canvasNode || !canvasNode.getContext){
console.warn("jumpBuffle,启用失败,canvas传参错误 或 浏览器不支持canvas");
this.error = true;
return false;
}
const t = this,
{ width, height } = canvasNode;
const _config = { //配置气泡冒泡设置
width: 30, //可自定义气泡宽度,高度随宽度变化
left : width/2 - 15, //距离左侧距离
top : height - 30, //距离顶部距离
alpha : 0.9, // 透明度设置
effect: 'ease',
speed: 'default',
max: 30, //画布上最多同时存在多少气泡, 默认30
isToAlapha: true,
diffWidth: 15, // 初始冒泡时气泡宽度与正常宽度的差值,默认15
cavHeight: height, // [非配置项] canvan标签的高度,设置气泡在不同高度有不同的浮动速度时会用到
cavWidth: width, // [非配置项] canvan标签的高度,设置气泡在左右晃动,触壁反弹时会用到
};
Object.assign(t, {
canvasInfo: {
canvas : canvasNode,
width,
height
},
config: Object.assign(_config, config),
ctx: canvasNode.getContext("2d"),
bubbleArr: [],//用来存储所有的气泡
allImg: { //缓存创建的img标签
lists: [], //src的列表
doms: [], //缓存list对应的创建的dom
loadState: [], //是否img标签已经加载完毕,如果完毕再直接返回img标签的dom,如果没有加载完毕则放到img.load函数内返回imgdom
}
});
callback && callback(t);
}
/*
** 冒泡的生命周期
** before: 气泡开始冒泡前
** after: 单个气泡消失后
*/
create(imgsrc, before, after){
const t = this,
{ctx, error} = t;
if(!ctx || error){
console.warn("jumpBuffle:create时,ctx错误");
return false;
}
const { bubbleArr, canvasInfo } = t,
{ width: imgwidth, max } = t.config;
t.createImg(imgsrc).then(imgNode => {
const imgInfo = {
el : imgNode,
width : imgwidth || imgNode.width,
height : imgwidth && imgNode.height*(imgwidth/imgNode.width) || imgNode.height
};
if(bubbleArr.length > max){
return;
}
bubbleArr.push(new DrawImg(ctx, imgInfo, t.config));
//每添加一个气泡触发一次的回调函数, 生命周期为气泡开始冒泡前
before && before(canvasInfo.canvas, imgNode, bubbleArr);
if(!t.setInter){
t.setInterFn(after);
}
});
return this;
}
createImg(imgsrc){
return new Promise(res => {
const { lists, doms, loadState } = this.allImg;
const i = lists.indexOf(imgsrc);
if(i > -1 && loadState[i]){
res(doms[i]);
return ;
}
const img = document.createElement('img');
img.src = imgsrc;
img.setAttribute('style', 'display:none;');
document.body.appendChild(img);
lists.push(imgsrc);
doms.push(img);
img.onload = () => {
loadState.push(true);
res(img);
}
})
}
setInterFn(after){
const t = this,
{ ctx, canvasInfo } = t,
{ width, height } = canvasInfo;
t.setInter = setInterval(function(){
try{
ctx.clearRect(0, 0, width, height);
t.bubbleArr = t.bubbleArr.filter(function(val){
val.addCtx();
val.updateCtx();
if(val.y < 10){
after && after();
return false;
}else{
return true;
}
});
if(t.bubbleArr.length === 0){
clearInterval(t.setInter);
t.setInter = null;
ctx.clearRect(0, 0, width, height);
}
}catch(e){
console.warn('创建态度气泡出错',e);
clearInterval(t.setInter);
}
},25);
}
} class DrawImg{
constructor(ctx, imgInfo, { left, top, alpha, speed, cavWidth, cavHeight, effect, isToAlapha, diffWidth }){
Object.assign(this, {
whichUnit: null, // 标识气泡在画布中的位置 ,canvan画布分成3个部分,2:中上、1:中、0:中下
ctx,
originWidth: imgInfo.width,
img: imgInfo.el,
imgWidth: imgInfo.width - diffWidth, //气泡初始大小与指定气泡大小的差值
imgHeight: imgInfo.height - diffWidth,
x: left, //气泡在x轴的位置 相对左侧
y: top, //气泡在y轴位置 相对顶部
alpha,
speed,
effect,
isToAlapha,
cavWidth,
oneUnit: cavHeight/4, //将canvan画布分成3个部分,中上、中、中下(中下占2/4,其他各占1/4)
toRight: (Math.random() > 0.5 ? false : true), //在气泡左右晃动效果时,该属性标识气泡是向左晃动还是向右晃动。
xPx: Math.random()*2.5, //x轴气泡每次位移像素数
yPx: null, //缓存气泡在画布y轴上每次位移的距离
yPxArr: null, //缓存y轴每次位移像素数的3个阶段的值的数组
diffAlapa: null, //缓存气泡在变为透明时每次增加的透明度
});
this.updateCtx = this.updateCtx.bind(this);
this.effectCommon = this.effectCommon.bind(this);
}
getSpeed(type = 'default'){
switch(type){
case 'slow':
return 0.6;
case 'fast':
return 1.4;
default:
return 1;
}
}
addCtx(){
const p = this,
{ ctx } = p;
ctx.save();
ctx.globalAlpha = p.alpha;
ctx.drawImage(p.img, p.x, p.y, p.imgWidth, p.imgHeight);
ctx.restore();
}
setImgWidth(){
const { originWidth, imgWidth } = this;
if(imgWidth < originWidth){//差值diffWidth的初始小气泡,逐渐变大为指定气泡的大小
this.imgWidth += 1;
this.imgHeight += 1;
}
}
setAlapa(){
const p = this,
{ y, isToAlapha, whichUnit } = p;
if(!isToAlapha)return false; //可自行配置,是否逐渐增加透明度
let diffAlapa = p.diffAlapa;
if(whichUnit === 2){ //气泡在画布中上部分时
if(!diffAlapa){
p.diffAlapa = diffAlapa = p.countDiffAlapa();
}
if(p.alpha <= diffAlapa){
p.alpha = 0;
}else{
p.alpha -= diffAlapa;
}
}
}
countDiffAlapa(){
const { alpha, oneUnit, yPx } = this;
return (alpha + 0.1)/(oneUnit/yPx);
}
setYpx(){
const p = this,
{ y, oneUnit } = p;
let i;
// 根据态度气泡在容器内到达高度不同,设置不同的速度
switch(true){
case y < oneUnit: //气泡在画布中上部分时
i = 2;
break;
case y > oneUnit && y < oneUnit*2: //气泡在画布的中部分时
i = 1;
break;
default: //气泡在画布的中下部分
i = 0;
}
if(p.whichUnit !== i){
p.yPx = p.yPxArr[i];
p.whichUnit = i;
}
p.y -= p.yPxArr[i];
}
setYpxArr(){
if(this.yPxArr)return false;
this.yPxArr = this.countYpxArr(this.effect, this.getSpeed(this.speed));
}
countYpxArr(effect, speedNum = 1){//根据effect不同,设置气泡在y轴上位移距离,数组包含3个值,分别表示在y轴的3个阶段的位移距离
const basePx = 2;
const easeArr = [basePx-0.5, basePx, basePx+0.5];
const linearArr = [basePx, basePx, basePx];
switch(effect){
case 'ease':
return mlt(easeArr);
case 'linear':
return mlt(linearArr);
default:
return mlt(easeArr);
}
function mlt(arr){
return arr.map(v => v*speedNum);
}
}
pullback(){//在x轴上,触壁反弹效果,晃动的上升,
const p = this,
{ y, cavWidth, xPx, originWidth } = p;
// 控制气泡左右晃动,触壁反弹效果
switch(true){
case (p.x + originWidth >= cavWidth): //气泡触右侧壁
p.toRight = false;
p.x -= xPx;
break;
case p.x <= 2: //气泡触左侧壁
p.toRight = true;
p.x += xPx;
break;
case p.toRight:
p.x += xPx;
break;
default:
p.x -= xPx;
}
this.effectCommon();
}
effectCommon(){
this.setYpxArr();
this.setYpx();
this.setAlapa();
this.setImgWidth();
}
updateCtx(){
this.pullback();
}
}

ES5版:

;(function(window){
function JumpBubble(opt){
var t = this,
canvas = opt.elCanvas,
canvasW = canvas.width,
canvasH = canvas.height;
if(!canvas){
console.warn("jumpBuffle:new 实例时,canvas传参错误");
return;
}
t.canvasInfo = {
canvas : canvas,
width : canvasW,
height : canvasH
};
var canvas = t.canvasInfo.canvas;
if(!canvas.getContext){
console.warn("jumpBuffle,启用失败,浏览器不支持canvas");
return;
}
var config = { //配置气泡冒泡设置
left : canvasW/2 - 15, //距离左侧距离
top : canvasH - 30, //距离顶部距离
alpha : 0.9 // 透明度设置
// width : 30 // 默认使用传入图片的实际宽高,可自定义气泡宽度,高度随宽度变化
};
t.callback = opt.callback; //每添加一个气泡触发一次的回调函数
t.config = hrExtend(config,opt.config);
t.ctx = canvas.getContext("2d");
t.bubbleArr = []; //用来存储所有的气泡
}; JumpBubble.prototype.create = function(opt){
var t = this,
bubbleArr = t.bubbleArr,
ctx = t.ctx,
img = opt.elImg,
config = t.config,
cfgImgWidth = config.width,
convasInfo = t.canvasInfo,
callback = t.callback;
if(!ctx){
console.warn("jumpBuffle:create时,ctx错误");
return;
}
var imgInfo = {
el : img,
width : cfgImgWidth || img.width,
height : cfgImgWidth && img.height*(cfgImgWidth/img.width) || img.height
};
if(bubbleArr.length>30){
return false;
}
bubbleArr.push(new drawImg(ctx,imgInfo,t.config,convasInfo));
//每添加一个气泡触发一次的回调函数,
// 参数1:canvas元素;参数2:传入的图片元素;参数3:当前存在的气泡数组
callback && callback(convasInfo.canvas,img,bubbleArr);
if(!t.setInter){
t.setInterFn();
}
}; JumpBubble.prototype.setInterFn = function(){
var t = this,
ctx = t.ctx,
convasInfo = t.canvasInfo,
canvasW = convasInfo.width,
canvasH = convasInfo.height;
t.setInter = setInterval(function(){
ctx.clearRect(0,0,canvasW,canvasH);
t.bubbleArr = t.bubbleArr.filter(function(val){
val.addCtx();
val.updateCtx();
if(val.y < 10){
return false;
}else{
return true;
}
});
if(t.bubbleArr.length === 0){
clearInterval(t.setInter);
t.setInter = null;
ctx.clearRect(0,0,canvasW,canvasH);
}
},25);
}; function drawImg(ctx,imgInfo,config ,canvasInfo){
var p = this;
p.ctx = ctx;
p.imgInfo = imgInfo,
p.img = imgInfo.el;
p.imgWidth = imgInfo.width - 10;
p.imgHeight = imgInfo.height - 10;
p.x = config.left;
p.y = config.top;
p.alpha = config.alpha;
p.canvasInfo = canvasInfo;
p.ranX = (Math.random()*5 - 2.5)/2;
}
drawImg.prototype.addCtx = function(){
var p = this,
ctx = p.ctx;
ctx.save();
ctx.globalAlpha = p.alpha;
ctx.drawImage(p.img,p.x,p.y,p.imgWidth, p.imgHeight);
ctx.restore();
}
drawImg.prototype.updateCtx = function(){
var p = this,
canvasInfo = p.canvasInfo,
afterRoad = canvasInfo.height/4,
ranX = p.ranX;
if(p.y < afterRoad){
if(Math.random() > 0.5){
p.x += ranX/2;
}
p.y -= 2.5;
if(p.alpha <= 0.02){
p.alpha = 0;
}else{
p.alpha -= 0.02;
}
}else if(p.y > afterRoad && p.y < afterRoad*2){
p.x += ranX/2;
p.y -= 3;
p.alpha -= 0.01;
}else{
p.x += ranX;
p.y -= 4;
}
if(p.imgWidth < p.imgInfo.width){
p.imgWidth += 1;
p.imgHeight += 1;
}
} function deepCopy(p,c){
/*@param p [必选] [对象] 被克隆对象
**c :[可选] p对象被克隆到c身上,c被改变
**返回值为深度克隆后的c*/
var c= c || {};
for(var i in p){
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
arguments.callee(p[i],c[i]);
} else {
c[i] = p[i];
}
}
return c;
};
// 至少传入2个参数,传入的参数都将会被深度复制,不会影响原对象,然后返回扩展后的新对象
function hrExtend() { //扩展对象
var args = arguments;
if (args.length < 2) return;
var temp = deepCopy(args[0]); //调用复制对象方法
for (var n = 1; n < args.length; n++) {
for (var i in args[n]) {
temp[i] = args[n][i];
}
}
return temp;
}
window.JumpBubble = JumpBubble;
})(window);

原生js实现canvas气泡冒泡效果的更多相关文章

  1. 使用原生js 实现点击消失效果

    JQ版 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title ...

  2. 利用tween,使用原生js实现模块回弹动画效果

    最近有一个需求,就是当屏幕往下一定像素时,下方会有一个隐藏的模块马上显现出来,向上运动后带有回弹效果.然后屏幕滚回去时这个模块能够原路返回 其实这个效果css3就可以很轻松实现,但是公司要求最低兼容i ...

  3. 原生JS实现幻灯片轮播效果

    在以往的认知中,一直以为用原生JS写轮播是件很难得事情,今天上班仿照网上的写了一个小demo.小试牛刀. 大致效果: html结构很简单,两个列表,一个代表图片列表,一个是右下角序号列表. <d ...

  4. 原生js简单实现拖拽效果

    实现弹窗拖拽效果的原理是:按下鼠标并移动——拖拽移动物体,抬起鼠标——停止移动.主要触发三个事件:onmousedown.onmousemove以及onmouseup: 首先搭建结构:一个宽350px ...

  5. 原生js实现Canvas实现拖拽式绘图,支持画笔、线条、箭头、三角形和圆形等等图形绘制功能,有实例Demo

    前言 需要用到图形绘制,没有找到完整的图形绘制实现,所以自己实现了一个 - - 演示地址:查看演示DEMO 新版本支持IE5+(你没看错,就是某软的IE浏览器)以上任意浏览器的Canvas绘图:htt ...

  6. 原生js实现简单的放大镜效果

    前言:相信很多同学在浏览购物网站的时候都会用到过放大镜的功能,这个功能在日常的网站也会经常用到.接下来我们开始实现一下它吧: (1)首先了解一下放大镜效果的html架构:如下图,它由两部分组成. ht ...

  7. 原生js之canvas时钟组件

    canvas一直是前端开发中不可或缺的一种用来绘制图形的标签元素,比如压缩上传的图片.比如刮刮卡.比如制作海报.图表插件等,很多人在面试的过程中也会被问到有没有接触过canvas图形绘制. 定义 ca ...

  8. 原生js 基于canvas写一个简单的前端 截图工具

    先看效果 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...

  9. 原生js实现图片轮播效果

    思路:设置父容器(一定宽度,一定高度,相对定位,子容器超出部分进行隐藏),子容器图片并排(浮动,绝对定位,每次点击进行相应的左或右偏移量) 1.html: <!DOCTYPE html> ...

随机推荐

  1. 开源摄影机:Axiom Camera

    一般情况下只有软件才有开源这个概念.这会儿发现了个很厉害的开源的产品:开源摄影机. 我还是第一次听说摄影机也可以开源.于是去该产品的官方网站了解了一下相关信息. 官网:http://axiom.ape ...

  2. Python学习笔记 - 函数参数

    >>> def power(x): ... return x * x ... >>> power(5) 25 >>> def power(x, n ...

  3. 使用JS取得焦点(focus)元素

    原文链接: Get the Focused Element with JavaScript 原文日期: 2014年3月19日 翻译日期: 2014年3月21日 翻译人员: 铁锚 对于良好的用户体验来说 ...

  4. Chrome浏览器开发调试系列(一)

    // 计划写一个 Chrome 浏览器以及 调试器的系列文章,我慢慢写. 边写边改,发觉博客真是个打草稿的好地方. // 本文针对的是当前最新的浏览器Chrome34,如果你的版本不够新,希望你能够更 ...

  5. The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files

    The type java.lang.Object cannot be resolved.It is indirectly referenced from required .class files ...

  6. XMPP系列(四)---发送和接收文字消息,获取历史消息功能

    今天开始做到最主要的功能发送和接收消息.获取本地历史数据. 先上到目前为止的效果图:              首先是要在XMPPFramework.h中引入数据存储模块: //聊天记录模块的导入 # ...

  7. IOS原声二维码条形码扫描实现

    本文讲述如何用系统自带的东东实现二维码扫描的功能:点击当前页面的某个按钮,创建扫描VIEW.细心的小伙伴可以发现 title被改变了,返回按钮被隐藏了.这个代码自己写就行了,与本文关系不大...绿色的 ...

  8. DB Query Analyzer 5.02 is distributed, 53 articles concerned have been published

    DB Query Analyzer is presented by Master Gen feng, Ma from Chinese Mainland. It has English version ...

  9. java keytool

    1.tomcat 配置Https,server.xml <Connector protocol="org.apache.coyote.http11.Http11Protocol&quo ...

  10. JDK 常用命令

    一) 引言:    当我们安装完JDK时,除了必须的编译运行以外,它就已经自带了很多辅助工具.正所谓“工欲善其事,必先利其器.”如果能用好这些工具,它们将大大方便你的开发.它们的实用和方便有时甚至会使 ...