说明:

本文章主要分为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. Linux grep命令分析以及C语言版本的实现

    1.作用 Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来.grep全称是Global Regular Expression Print,表示全 ...

  2. [转]smail语法 详解

    大家都应该知道APK文件其实就是一个MIME为ZIP的压缩包,我们修改ZIP后缀名方式可以看到内部的文件结构,例如修改后缀后用RAR打开鳄鱼小顽皮APK能看到的是(Google Play下载的完整版版 ...

  3. Java-ServletRequestEvent-ServletRequestAttributeEvent

    /** * Events of this kind indicate lifecycle * events for a ServletRequest. * The source of the even ...

  4. ubuntu如何添加新的PPA

    首先要知道PPA源地址,比如: ppa:gwibber-daily/ppa 然后用apt-get指令添加: sudo add-apt-repository ppa:gwibber-daily/ppa ...

  5. 【57】android图片印刻,阳刻,素描图效果处理

    介绍我参与开发的妙趣剪纸app使用的图片处理相关的技术 关于妙趣剪纸,各大android商店都可以下载,下面贴出小米商店的链接 妙趣剪纸下载 软件效果截图 如何实现上面的图片处理效果呢 1.初始化高斯 ...

  6. LeetCode之“动态规划”:Decode Ways

    题目链接 题目要求: A message containing letters from A-Z is being encoded to numbers using the following map ...

  7. LeetCode之“动态规划”:Climbing Stairs

    题目链接 题目要求 You are climbing a stair case. It takes n steps to reach to the top. Each time you can eit ...

  8. DB Query Analyzer 6.02 is released, 71 articles concerned have been published

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

  9. iOS中获取本地通讯录联系人以及汉字首字母排序

    iOS中获取手机通讯录中的联系人信息: /*** 加载本地联系人*/ - (void)loadLocalContacts { //新建一个通讯录类 ABAddressBookRef addressBo ...

  10. 设计模式学习--组合模式,c++代码

    下面是组合模式的UML类图: <span style="font-family:Microsoft YaHei;font-size:18px;"><span st ...