爱心

  • 1. 案例简介
  • 2. 爱心实现
    • 2.1 盒子模型
    • 2.2 CSS样式
    • 2.3 JS实现动效
  • 3. 完整源码

1. 案例简介

最近网剧《点燃我,温暖你》热映,相信广大男同胞的女朋友都在吵着要李洵的❤,没有女朋友的兄弟也要注意,这机会它不就来了吗,博主在刷短视频时也看了部分剪辑,当时震惊我的是那句台词“这次老师布置的可是求三个数里的最大值,你不会是怕自己做不出来所以不敢交作业吧。”这句多少是有些搞笑了,后来也有刷到更新剧集的,就是C语言考试时,考试题目是打印爱心❤,男主写了个动态爱心❤,给女主看懵了,当时就有不祥的预感,果然下午就有人找我要我给她弄一个,C语言咱真做不到,前端的可以试试,这就来上!完整源码在最后,复制就可用(点击目录直接跳转)!
效果如图

2. 爱心实现

这个案例其实只是简单的一个盒子模型,关键是使用js让这个爱心实现动效,包括爱心边界要由许多跳动的小心心组成,以及在不断缩小实现跳动效果。

2.1 盒子模型

首先自然是一个盒子模型打底,在其中实现爱心的效果

<canvas id="pinkboard"></canvas>

2.2 CSS样式

CSS也比较简单,只是简单的边界修订,包括背景颜色(黑色),大小,宽高等等有的没的。

html, body {height: 100%;padding: 0;margin: 0;background: #000;
}
canvas {position: absolute;width: 100%;height: 100%;
}

2.3 JS实现动效

这个就比较复杂啦,包括动效的方向,动效范围,动效行为类型以及定时等等,利用回调函数实现宽高自适应,因为爱心是要跳动的,大小会改变,这就要求宽高要一起变化,否则会出现特变尴尬的效果,你的爱心可能会比较抽象,,,避免此问题也很简单,直接让宽高产生联系即可,他们一起改变!

范围指定

var Point = (function() {function Point(x, y) {this.x = (typeof x !== 'undefined') ? x : 0;this.y = (typeof y !== 'undefined') ? y : 0;}Point.prototype.clone = function() {return new Point(this.x, this.y);};Point.prototype.length = function(length) {if (typeof length == 'undefined')return Math.sqrt(this.x * this.x + this.y * this.y);this.normalize();this.x *= length;this.y *= length;return this;};Point.prototype.normalize = function() {var length = this.length();this.x /= length;this.y /= length;return this;};return Point;
})();

大致爱心形状
简单画出大致形状,后面去进一步改进边框,让边框也实现动效,这里的关键就是爱心的凹下去的部分,可以把爱心看成是一个圆形,只是发生了一些简单的改变,凹下去一块,突出去一块。

var Particle = (function() {function Particle() {this.position = new Point();this.velocity = new Point();this.acceleration = new Point();this.age = 0;}Particle.prototype.initialize = function(x, y, dx, dy) {this.position.x = x;this.position.y = y;this.velocity.x = dx;this.velocity.y = dy;this.acceleration.x = dx * settings.particles.effect;this.acceleration.y = dy * settings.particles.effect;this.age = 0;};Particle.prototype.update = function(deltaTime) {this.position.x += this.velocity.x * deltaTime;this.position.y += this.velocity.y * deltaTime;this.velocity.x += this.acceleration.x * deltaTime;this.velocity.y += this.acceleration.y * deltaTime;this.age += deltaTime;};Particle.prototype.draw = function(context, image) {function ease(t) {return (--t) * t * t + 1;}var size = image.width * ease(this.age / settings.particles.duration);context.globalAlpha = 1 - this.age / settings.particles.duration;context.drawImage(image, this.position.x - size / 2, this.position.y - size / 2, size, size);};return Particle;
})();

边界小爱心

这个实现最为麻烦,要设置小爱心的不断更替,以及形状渐变等等,设置定时器,保证每个小爱心的出现时间有规律,这样看起来才会整齐,否则总会有点尴尬的小坚强一直不消散,还有就是要保证小爱心扩散的方向,要结合它所处的位置,如果都是直接朝着一个方向扩散会感觉很奇怪,不符合人类的直观审美,在那个位置的小爱心,就要从那个位置出发去消散,避免诙谐的感受。同时要随着大爱心的大小改变调整自己的出现位置,但是也不能全部出现在内侧,还要有一两个漏网之鱼在外面,有种凌乱美,看起来观感更佳。

var ParticlePool = (function() {var particles,firstActive = 0,firstFree   = 0,duration    = settings.particles.duration;function ParticlePool(length) {// create and populate particle poolparticles = new Array(length);for (var i = 0; i < particles.length; i++)particles[i] = new Particle();}ParticlePool.prototype.add = function(x, y, dx, dy) {particles[firstFree].initialize(x, y, dx, dy);// handle circular queuefirstFree++;if (firstFree   == particles.length) firstFree   = 0;if (firstActive == firstFree       ) firstActive++;if (firstActive == particles.length) firstActive = 0;};ParticlePool.prototype.update = function(deltaTime) {var i;// update active particlesif (firstActive < firstFree) {for (i = firstActive; i < firstFree; i++)particles[i].update(deltaTime);}if (firstFree < firstActive) {for (i = firstActive; i < particles.length; i++)particles[i].update(deltaTime);for (i = 0; i < firstFree; i++)particles[i].update(deltaTime);}// remove inactive particleswhile (particles[firstActive].age >= duration && firstActive != firstFree) {firstActive++;if (firstActive == particles.length) firstActive = 0;}};ParticlePool.prototype.draw = function(context, image) {// draw active particlesif (firstActive < firstFree) {for (i = firstActive; i < firstFree; i++)particles[i].draw(context, image);}if (firstFree < firstActive) {for (i = firstActive; i < particles.length; i++)particles[i].draw(context, image);for (i = 0; i < firstFree; i++)particles[i].draw(context, image);}};return ParticlePool;
})();

将上述部件组装
这里就还是比较简单的啦,只需要把之前写好的部分按照预想该怎么调用就怎么调用,设置好定时器以及出现位置,因为是心形,还要结合一些数学函数,比如cos等,实现变角处理,让整个心形更加圆滑平稳,还要设置页面的显示方式,渐变出现,以及出现的速度等等。

(function(canvas) {var context = canvas.getContext('2d'),particles = new ParticlePool(settings.particles.length),particleRate = settings.particles.length / settings.particles.duration, // particles/sectime;// get point on heart with -PI <= t <= PIfunction pointOnHeart(t) {return new Point(160 * Math.pow(Math.sin(t), 3),130 * Math.cos(t) - 50 * Math.cos(2 * t) - 20 * Math.cos(3 * t) - 10 * Math.cos(4 * t) + 25);}// creating the particle image using a dummy canvasvar image = (function() {var canvas  = document.createElement('canvas'),context = canvas.getContext('2d');canvas.width  = settings.particles.size;canvas.height = settings.particles.size;// helper function to create the pathfunction to(t) {var point = pointOnHeart(t);point.x = settings.particles.size / 2 + point.x * settings.particles.size / 350;point.y = settings.particles.size / 2 - point.y * settings.particles.size / 350;return point;}// create the pathcontext.beginPath();var t = -Math.PI;var point = to(t);context.moveTo(point.x, point.y);while (t < Math.PI) {t += 0.01; // baby steps!point = to(t);context.lineTo(point.x, point.y);}context.closePath();// create the fillcontext.fillStyle = '#ea80b0';context.fill();// create the imagevar image = new Image();image.src = canvas.toDataURL();return image;})();

边界处理以及其他设定
这里要设定好小爱心们散落的位置以及出现地址,保证跟随着大爱心一起动,同时圈定大爱心的位置。

// render that thing!function render() {// next animation framerequestAnimationFrame(render);// update timevar newTime   = new Date().getTime() / 1000,deltaTime = newTime - (time || newTime);time = newTime;// clear canvascontext.clearRect(0, 0, canvas.width, canvas.height);// create new particlesvar amount = particleRate * deltaTime;for (var i = 0; i < amount; i++) {var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());var dir = pos.clone().length(settings.particles.velocity);particles.add(canvas.width / 2 + pos.x, canvas.height / 2 - pos.y, dir.x, -dir.y);}// update and draw particlesparticles.update(deltaTime);particles.draw(context, image);}// handle (re-)sizing of the canvasfunction onResize() {canvas.width  = canvas.clientWidth;canvas.height = canvas.clientHeight;}window.onresize = onResize;// delay rendering bootstrapsetTimeout(function() {onResize();render();}, 10);
})(document.getElementById('pinkboard'));

3. 完整源码

相信也有同志可能不是程序员,但被迫找到这里,这个源码只需要复制到一个空的txt文件里,然后把txt的后缀改成html打开就可以啦。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE> New Document </TITLE><META NAME="Generator" CONTENT="EditPlus"><META NAME="Author" CONTENT=""><META NAME="Keywords" CONTENT=""><META NAME="Description" CONTENT=""><style>html, body {height: 100%;padding: 0;margin: 0;background: #000;
}
canvas {position: absolute;width: 100%;height: 100%;
}</style></HEAD><BODY><canvas id="pinkboard"></canvas><script>/** Settings*/
var settings = {particles: {length:   500, // maximum amount of particlesduration:   2, // particle duration in secvelocity: 100, // particle velocity in pixels/seceffect: -0.75, // play with this for a nice effectsize:      30, // particle size in pixels},
};/** RequestAnimationFrame polyfill by Erik Möller*/
(function(){var b=0;var c=["ms","moz","webkit","o"];for(var a=0;a<c.length&&!window.requestAnimationFrame;++a){window.requestAnimationFrame=window[c[a]+"RequestAnimationFrame"];window.cancelAnimationFrame=window[c[a]+"CancelAnimationFrame"]||window[c[a]+"CancelRequestAnimationFrame"]}if(!window.requestAnimationFrame){window.requestAnimationFrame=function(h,e){var d=new Date().getTime();var f=Math.max(0,16-(d-b));var g=window.setTimeout(function(){h(d+f)},f);b=d+f;return g}}if(!window.cancelAnimationFrame){window.cancelAnimationFrame=function(d){clearTimeout(d)}}}());/** Point class*/
var Point = (function() {function Point(x, y) {this.x = (typeof x !== 'undefined') ? x : 0;this.y = (typeof y !== 'undefined') ? y : 0;}Point.prototype.clone = function() {return new Point(this.x, this.y);};Point.prototype.length = function(length) {if (typeof length == 'undefined')return Math.sqrt(this.x * this.x + this.y * this.y);this.normalize();this.x *= length;this.y *= length;return this;};Point.prototype.normalize = function() {var length = this.length();this.x /= length;this.y /= length;return this;};return Point;
})();/** Particle class*/
var Particle = (function() {function Particle() {this.position = new Point();this.velocity = new Point();this.acceleration = new Point();this.age = 0;}Particle.prototype.initialize = function(x, y, dx, dy) {this.position.x = x;this.position.y = y;this.velocity.x = dx;this.velocity.y = dy;this.acceleration.x = dx * settings.particles.effect;this.acceleration.y = dy * settings.particles.effect;this.age = 0;};Particle.prototype.update = function(deltaTime) {this.position.x += this.velocity.x * deltaTime;this.position.y += this.velocity.y * deltaTime;this.velocity.x += this.acceleration.x * deltaTime;this.velocity.y += this.acceleration.y * deltaTime;this.age += deltaTime;};Particle.prototype.draw = function(context, image) {function ease(t) {return (--t) * t * t + 1;}var size = image.width * ease(this.age / settings.particles.duration);context.globalAlpha = 1 - this.age / settings.particles.duration;context.drawImage(image, this.position.x - size / 2, this.position.y - size / 2, size, size);};return Particle;
})();/** ParticlePool class*/
var ParticlePool = (function() {var particles,firstActive = 0,firstFree   = 0,duration    = settings.particles.duration;function ParticlePool(length) {// create and populate particle poolparticles = new Array(length);for (var i = 0; i < particles.length; i++)particles[i] = new Particle();}ParticlePool.prototype.add = function(x, y, dx, dy) {particles[firstFree].initialize(x, y, dx, dy);// handle circular queuefirstFree++;if (firstFree   == particles.length) firstFree   = 0;if (firstActive == firstFree       ) firstActive++;if (firstActive == particles.length) firstActive = 0;};ParticlePool.prototype.update = function(deltaTime) {var i;// update active particlesif (firstActive < firstFree) {for (i = firstActive; i < firstFree; i++)particles[i].update(deltaTime);}if (firstFree < firstActive) {for (i = firstActive; i < particles.length; i++)particles[i].update(deltaTime);for (i = 0; i < firstFree; i++)particles[i].update(deltaTime);}// remove inactive particleswhile (particles[firstActive].age >= duration && firstActive != firstFree) {firstActive++;if (firstActive == particles.length) firstActive = 0;}};ParticlePool.prototype.draw = function(context, image) {// draw active particlesif (firstActive < firstFree) {for (i = firstActive; i < firstFree; i++)particles[i].draw(context, image);}if (firstFree < firstActive) {for (i = firstActive; i < particles.length; i++)particles[i].draw(context, image);for (i = 0; i < firstFree; i++)particles[i].draw(context, image);}};return ParticlePool;
})();/** Putting it all together*/
(function(canvas) {var context = canvas.getContext('2d'),particles = new ParticlePool(settings.particles.length),particleRate = settings.particles.length / settings.particles.duration, // particles/sectime;// get point on heart with -PI <= t <= PIfunction pointOnHeart(t) {return new Point(160 * Math.pow(Math.sin(t), 3),130 * Math.cos(t) - 50 * Math.cos(2 * t) - 20 * Math.cos(3 * t) - 10 * Math.cos(4 * t) + 25);}// creating the particle image using a dummy canvasvar image = (function() {var canvas  = document.createElement('canvas'),context = canvas.getContext('2d');canvas.width  = settings.particles.size;canvas.height = settings.particles.size;// helper function to create the pathfunction to(t) {var point = pointOnHeart(t);point.x = settings.particles.size / 2 + point.x * settings.particles.size / 350;point.y = settings.particles.size / 2 - point.y * settings.particles.size / 350;return point;}// create the pathcontext.beginPath();var t = -Math.PI;var point = to(t);context.moveTo(point.x, point.y);while (t < Math.PI) {t += 0.01; // baby steps!point = to(t);context.lineTo(point.x, point.y);}context.closePath();// create the fillcontext.fillStyle = '#ea80b0';context.fill();// create the imagevar image = new Image();image.src = canvas.toDataURL();return image;})();// render that thing!function render() {// next animation framerequestAnimationFrame(render);// update timevar newTime   = new Date().getTime() / 1000,deltaTime = newTime - (time || newTime);time = newTime;// clear canvascontext.clearRect(0, 0, canvas.width, canvas.height);// create new particlesvar amount = particleRate * deltaTime;for (var i = 0; i < amount; i++) {var pos = pointOnHeart(Math.PI - 2 * Math.PI * Math.random());var dir = pos.clone().length(settings.particles.velocity);particles.add(canvas.width / 2 + pos.x, canvas.height / 2 - pos.y, dir.x, -dir.y);}// update and draw particlesparticles.update(deltaTime);particles.draw(context, image);}// handle (re-)sizing of the canvasfunction onResize() {canvas.width  = canvas.clientWidth;canvas.height = canvas.clientHeight;}window.onresize = onResize;// delay rendering bootstrapsetTimeout(function() {onResize();render();}, 10);
})(document.getElementById('pinkboard'));</script></BODY>
</HTML>

【前端小案例】浪漫动态爱心相关推荐

  1. 前端实战小案例--3D动态分层图片

    3D动态分层图片 想练习更多前端案例,请进个人主页,点击前端实战案例->传送门 觉得不错的记得点个赞?支持一下我0.0!谢谢了! 效果图:(图不是重点,重点在代码里面(*^__^*) 嘻嘻) 代 ...

  2. 前端小案例之3D导航栏

    简单3D导航栏案例-鼠标经过盒子时底部盒子旋转 最近入门学习前端用简单的html结构加CSS样式写了个3D导航栏的小案例在这里分享给大家. 文章目录 简单3D导航栏案例-鼠标经过盒子时底部盒子旋转 一 ...

  3. 前端小案例--android机器人

    这是CSS部分 *{padding: 0;margin: 0;}body{background-color: #ccc;}.content{width: 500px;height: 600px;bor ...

  4. 30个HTML+CSS前端开发案例(一)

    30个前端小案例(1-5) 三角形 百度新闻热榜 仿小米商城左侧菜单 产品展示效果 精美分页效果 三角形 <!DOCTYPE html> <html><head>& ...

  5. c语言满屏爱心,微信聊天可以发满屏动态爱心了 个性又浪漫!

    前不久,分享过一个<微信深色模式隐藏表白技巧>,受到不少小伙伴欢迎.这种隐藏表白模式的神奇之处在于,只有在微信深色模式下,才能显示出隐藏字,浅色模式是隐藏的,另外支持双含义显示,个性又有创 ...

  6. 前端程序员的浪漫动态告白表白女友源码

    动态告白表白女友源码前端程序员的浪漫,是一款网页动态表白源码,直接点击html看效果,浏览器打开就好了,兄弟们可以上车了. 特别说明: 1.文字在 take.js 里面改 或者 index.html里 ...

  7. 一个mybatis动态 SQL查询的完整小案例。包含多表联合查询。

    多表联合查询 一个根据机场查询航线的例子.有两张表,机场表包含机场信息:机场id.机场名字.机场城市. 航班包含航线信息:航班id.飞机编号.飞行时间.票价.起飞机场id.降落机场id. 需要查询的结 ...

  8. 前端实战小案例--canvas实战之FlappyBird小游戏

    前端实战小案例--canvas实战之FlappyBird小游戏 想练习更多前端案例,请进个人主页,点击前端实战案例->传送门 觉得不错的记得点个赞?支持一下我0.0!谢谢了! 不积跬步无以至千里 ...

  9. 在vue项目前端如何实现展示动态小程序二维码

    在vue项目前端如何实现展示动态小程序二维码 (就是根据所对应的商家数据展示一个动态的二维码) 首先,主要的工作还是后端和小程序前端的工作,我们vue项目的前端主要是根据他们给出的url一级所对应的参 ...

最新文章

  1. 三代测序关键计算技术开发及应用
  2. postman中POST请求时参数包含参数list设置
  3. iOS经典面试题之分析GCD的dispatch_group任务执行问题
  4. eclipse启动不了并报错Java was started but exit with code 13 问题解决
  5. babylonjs 设置面板位置_一篇关于开关面板的详细集合,值得收藏转发
  6. 香港买thinkpad大陆使用,大陆可以享受售后服务吗
  7. 一个符号引发的讨论,对抗攻击算法FGSM的纯粹版:FGNS,附代码
  8. 计算机毕设分词,毕业设计(论文)+计算机科学与技术+中文分词方法研究与实现论文全文.doc...
  9. Web前端基础---JQuery的页面加载+选择器+电子时钟案例
  10. 基于CNN的中文文本分类算法(可应用于垃圾文本过滤、情感分析等场景)
  11. thymeleaf获取url地址跳转时所带参数
  12. Atitit.一个cms有多少少扩展点,多少api wordpress  cms有多少api。。扩展点
  13. 合作、高效 -- 下一代汽车电子软件开发与测试论坛
  14. selenium官网下载地址
  15. 在本地计算机无法启动iis admin 服务.错误:1053,Windows无法在本地计算机上启动SQL Server(SQLEXPRESS)服务。错误1053...
  16. jenkins 下载 安装 启动教程-通过war的方式
  17. 儿研所 计算机训练,首都儿研所自闭症训练中心成立
  18. CCF认证 2019-03 01小中大
  19. mysql analyze_MySQL中的analyze与optimize
  20. idea remote debug

热门文章

  1. 猫咪记单词——NABCD模型分析
  2. python微信好友分析_Python简单分析微信好友
  3. 服务器 芯片型号查询,查询服务器:OS、CPU、内存、硬盘信息
  4. win10 关键错误,(win10开始菜单和cortana无法工作)解决办法
  5. Unity 屏幕特效 之 简单地调整颜色的亮度、饱和度、对比度
  6. 淘宝天猫分进合击打造新零售基座 年轻一代管理者出任总裁
  7. java 8 lamdba调试工具_Java8-21-lambda 测试调试
  8. PHP保留两位小数显示
  9. 在控制台使用js获取cookie信息
  10. 网络连接的获取,禁用,启用(VC++)