当初男票就是用 canvas 画一场流星雨把我追到手的

2023-11-23 02:40

本文主要是介绍当初男票就是用 canvas 画一场流星雨把我追到手的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

扫码关注添加客服

进Python社群▲

扫码关注添加客服

进Java社群

作者丨Chaz

源自:

https://segmentfault.com/a/1190000008664249

开始

妹子都喜欢流星,如果她说不喜欢,那她一定是一个假妹子。

现在就一起来做一场流星雨,用程序员的野路子浪漫一下。

要画一场流星雨,首先,自然我们要会画一颗流星。

玩过 canvas 的同学,你画圆画方画线条这么 6,如果说叫你画下面这个玩意儿,你会不会觉得你用的是假 canvas?canvas 没有画一个带尾巴玩意儿的 api 啊。

画一颗流星

是的,的却是没这个 api,但是不代表我们画不出来。流星就是一个小石头,然后因为速度过快产生大量的热量带动周围的空气发光发热,所以经飞过的地方看起来就像是流星的尾巴,我们先研究一下流星这个图像,整个流星处于他自己的运动轨迹之中,当前的位置最亮,轮廓最清晰,而之前划过的地方离当前位置轨迹距离越远就越暗淡越模糊。

上面的分析结果很关键, canvas 上是每一帧就重绘一次,每一帧之间的时间间隔很短。流星经过的地方会越来越模糊最后消失不见,那有没有可以让画布画的图像每过一帧就变模糊一点而不是全部清除的办法?如果可以这样,就可以把每一帧用线段画一小段流星的运动轨迹,最后画出流星的效果。

骗纸!你也许会说,这那里像流星了???

别急,让我多画几段给你看看。

什么?还是不像?我们把它画小点,这下总该像了把?

上面几幅图我是在 ps 上模拟的,本质上 ps 也是在画布上绘画,我们马上在 canvas 上试试。

那,直接代码实现一下。

// 坐标
classCrood {constructor(x=0, y=0) {this.x = x;this.y = y;}setCrood(x, y) {this.x = x;this.y = y;}copy() {returnnewCrood(this.x, this.y);}
}
// 流星
classShootingStar {constructor(init=newCrood, final=newCrood, size=3, speed=200, onDistory=null) {this.init = init; // 初始位置this.final = final; // 最终位置this.size = size; // 大小this.speed = speed; // 速度:像素/s// 飞行总时间this.dur = Math.sqrt(Math.pow(this.final.x-this.init.x, 2) + Math.pow(this.final.y-this.init.y, 2)) * 1000 / this.speed;this.pass = 0; // 已过去的时间this.prev = this.init.copy(); // 上一帧位置this.now = this.init.copy(); // 当前位置this.onDistory = onDistory;}draw(ctx, delta) {this.pass += delta;this.pass = Math.min(this.pass, this.dur);let percent = this.pass / this.dur;this.now.setCrood(this.init.x + (this.final.x - this.init.x) * percent,this.init.y + (this.final.y - this.init.y) * percent);// canvasctx.strokeStyle = '#fff';ctx.lineCap = 'round';ctx.lineWidth = this.size;ctx.beginPath();ctx.moveTo(this.now.x, this.now.y);ctx.lineTo(this.prev.x, this.prev.y);ctx.stroke();this.prev.setCrood(this.now.x, this.now.y);if (this.pass === this.dur) {this.distory();}}distory() {this.onDistory && this.onDistory();}
}
// effet
let cvs = document.querySelector('canvas');
let ctx = cvs.getContext('2d');
let T;
let shootingStar = newShootingStar(newCrood(100, 100),newCrood(400, 400),3,200,()=>{cancelAnimationFrame(T)});
let tick = (function() {let now = (newDate()).getTime();let last = now;let delta;returnfunction() {delta = now - last;delta = delta > 500 ? 30 : (delta < 16? 16 : delta);last = now;// console.log(delta);T = requestAnimationFrame(tick);ctx.save();ctx.fillStyle = 'rgba(0,0,0,0.2)'; // 每一帧用 “半透明” 的背景色清除画布ctx.fillRect(0, 0, cvs.width, cvs.height);ctx.restore();shootingStar.draw(ctx, delta);}
})();
tick();

效果:一颗流星

sogoyi 快看,一颗活泼不做作的流星!!! 是不是感觉动起来更加逼真一点?

流星雨

我们再加一个流星雨 MeteorShower 类,生成多一些随机位置的流星,做出流星雨。

// 坐标
classCrood {constructor(x=0, y=0) {this.x = x;this.y = y;}setCrood(x, y) {this.x = x;this.y = y;}copy() {returnnewCrood(this.x, this.y);}
}
// 流星
classShootingStar {constructor(init=newCrood, final=newCrood, size=3, speed=200, onDistory=null) {this.init = init; // 初始位置this.final = final; // 最终位置this.size = size; // 大小this.speed = speed; // 速度:像素/s// 飞行总时间this.dur = Math.sqrt(Math.pow(this.final.x-this.init.x, 2) + Math.pow(this.final.y-this.init.y, 2)) * 1000 / this.speed;this.pass = 0; // 已过去的时间this.prev = this.init.copy(); // 上一帧位置this.now = this.init.copy(); // 当前位置this.onDistory = onDistory;}draw(ctx, delta) {this.pass += delta;this.pass = Math.min(this.pass, this.dur);let percent = this.pass / this.dur;this.now.setCrood(this.init.x + (this.final.x - this.init.x) * percent,this.init.y + (this.final.y - this.init.y) * percent);// canvasctx.strokeStyle = '#fff';ctx.lineCap = 'round';ctx.lineWidth = this.size;ctx.beginPath();ctx.moveTo(this.now.x, this.now.y);ctx.lineTo(this.prev.x, this.prev.y);ctx.stroke();this.prev.setCrood(this.now.x, this.now.y);if (this.pass === this.dur) {this.distory();}}distory() {this.onDistory && this.onDistory();}
}
classMeteorShower {constructor(cvs, ctx) {this.cvs = cvs;this.ctx = ctx;this.stars = [];this.T;this.stop = false;this.playing = false;}createStar() {let angle = Math.PI / 3;let distance = Math.random() * 400;let init = newCrood(Math.random() * this.cvs.width|0, Math.random() * 100|0);let final = newCrood(init.x + distance * Math.cos(angle), init.y + distance * Math.sin(angle));let size = Math.random() * 2;let speed = Math.random() * 400 + 100;let star = newShootingStar(init, final, size, speed,()=>{this.remove(star)});return star;}remove(star) {this.stars = this.stars.filter((s)=>{ return s !== star});}update(delta) {if (!this.stop && this.stars.length < 20) {this.stars.push(this.createStar());}this.stars.forEach((star)=>{star.draw(this.ctx, delta);});}tick() {if (this.playing) return;this.playing = true;let now = (newDate()).getTime();let last = now;let delta;let  _tick = ()=>{if (this.stop && this.stars.length === 0) {cancelAnimationFrame(this.T);this.playing = false;return;}delta = now - last;delta = delta > 500 ? 30 : (delta < 16? 16 : delta);last = now;// console.log(delta);this.T = requestAnimationFrame(_tick);ctx.save();ctx.fillStyle = 'rgba(0,0,0,0.2)'; // 每一帧用 “半透明” 的背景色清除画布ctx.fillRect(0, 0, cvs.width, cvs.height);ctx.restore();this.update(delta);}_tick();}start() {this.stop = false;this.tick();}stop() {this.stop = true;}  
}
// effet
let cvs = document.querySelector('canvas');
let ctx = cvs.getContext('2d');
let meteorShower = newMeteorShower(cvs, ctx);
meteorShower.start();

效果:流星雨

透明背景

先不急着激动,这个流星雨有点单调,可以看到上面的代码中,每一帧,我们用了透明度为 0.2 的黑色刷了一遍画布,背景漆黑一片,如果说我们的需求是透明背景呢?

比如,我们要用这个夜景图片做背景,然后在上面加上我们的流星,我们每一帧刷一层背景的小伎俩就用不了啦。因为我们要保证除开流星之外的部分,应该是透明的。

这里就要用到一个冷门的属性了,globalCompositeOperation,全局组合操作?原谅我放荡不羁的翻译。

这个属性其实就是用来定义后绘制的图形与先绘制的图形之间的组合显示效果的。 

他可以设置这些值

这些属性说明没必要仔细看,更不用记下来,直接看 api 示例 运行效果就很清楚了。示例里,先绘制的是填充正方形,后绘制的是填充圆形。

是不是豁然开朗,一目了然?

对于我们来说,原图像是每一帧画完的所有流星,目标图像是画完流星之后半透明覆盖画布的黑色矩形。而我们每一帧要保留的就是,上一帧 0.8 透明度的流星,覆盖画布黑色矩形我们不能显示。

注意这里的 destination-out 和 destination-in,示例中这两个属性最终都只有部分源图像保留了下来,符合我们只要保留流星的需求。我觉得 w3cschool 上描述的不是很正确,我用我自己的理解概括一下。

  • destination-in :只保留了源图像(矩形)和目标图像(圆)交集区域的源图像

  • destination-out:只保留了源图像(矩形)减去目标图像(圆)之后区域的源图像

上述示例目标图像的透明度是 1,源图像被减去的部分是完全不见了。而我们想要的是他可以按照目标透明度进行部分擦除。改一下示例里的代码看看是否支持半透明的计算。

看来这个属性支持半透明的计算。源图像和目标图像交叠的部分以半透明的形式保留了下来。也就是说如果我们要保留 0.8 透明度的流星,可以这样设置 globalCompositeOperation

ctx.fillStyle = 'rgba(0,0,0,0.8)'
globalCompositeOperation = 'destination-in';
ctx.fillRect(0, 0, cvs.width, cvs.height);
// 或者
ctx.fillStyle = 'rgba(0,0,0,0.2)'
globalCompositeOperation = 'destination-out';
ctx.fillRect(0, 0, cvs.width, cvs.height);
最终效果

加上 globalCompositeOperation 之后的效果既最终效果:

快约上你的妹子看流星雨吧。

...

什么?你没有妹子?

作者丨Chaz

源自:

https://segmentfault.com/a/1190000008664249

声明:文章著作权归作者所有,如有侵权,请联系小编删除

程序员专栏 扫码关注填加客服 长按识别下方二维码进群

近期精彩内容推荐:  

 阿里、京东、美团……背后的共同金主

 沸腾了!苏宁全员涨薪,每月最高多1万6!

 大白话java多线程,高手勿入

 用Python完成Excel合并(拆分)的各种操作


在看点这里好文分享给更多人↓↓

这篇关于当初男票就是用 canvas 画一场流星雨把我追到手的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/414764

相关文章

HTML5将会带来一场Web革命

一个不起眼的对网页的标记机制为何有这么大的影响? HTML5 引起的广泛关注是否仅仅是一时的科技狂热? 总之,为什么计算机专家需要关心这个呢?   AD:2013云计算架构师峰会课程资料下载   面向万维网(WWW)的软件开发人员认为新的 HTML5 规范正在革新现有 Web 系统的呈现、工作、使用方式。他们说 HTML5 简化了程序员的工作,使访问多种多样的设备和应用程序更加方便,并且提

一生中错过多少次,就像昨夜下了一场雨,而我却在睡梦中

许多的事情,总是在经历过以后才会懂得。一如感情,痛过了,才会懂得如何保护自己;傻过了,才会懂得适时地坚持与放弃,在得到与失去中我们慢慢地认识自己。既然默默相守已失去意义,莫不如立即斩断心中那情思屡屡,放弃你所珍爱的,期待的,重新选择。其实,生活并不需要这么些无谓的执着,没有什么就真的不能割舍。学会放弃,生活会更容易。

影响画布微信小程序canvas及skyline和webview用户界面布局的关键流程

影响微信小程序画布canvas及skyline和webview用户界面布局的关键流程 目录 影响微信小程序画布canvas及skyline和webview用户界面布局的关键流程 一、微信小程序canvas开发流程 1.1、官方指南 1.2、客制化开发 第一步:在 WXML 中添加 canvas 组件 第二步:获取 Canvas 对象和渲染上下文 第三步 画布#ID选择器执行回调——

Android canvas save restore saveLayer的异同点

一、基础操作 drawText、drawRect、drawColor等 对于这些基础操作,相信每一个安卓开发者都能说上个一二点出来,这些就不多做介绍,api 工程师必备技能之一。 在进阶之前,先回答这个问题:    问:canvas既然大家都理解为画布,那如果先在画布上绘制了某些内容,然后再canvas.rotate旋转了画布,为什么这些已经绘制在画布上的内容不会跟随着旋转?    答:由此可

自定义View-Canvas

转载自:https://www.jianshu.com/p/f69873371763 Android Canvas 方法总结 简介 在自定义 View的时候,我们经常需要绘制一些自己想要的效果。 这里就需要使用Canvas对象。 下面将Canvas对象常用方法做个笔记,方便记忆。 对Canvas进行操作 对Canvas的一系列操作,是指对Canvas进行旋转、平移、缩放等操作。 这些操作可以

canvas-实现放大镜效果

canvas-实现放大镜效果 目录 文章目录 前言推荐阅读代码展示结果展示 前言 本文为canvas实现放大镜逻辑简单,适合作为基础项目练手 推荐阅读 《H5 canvas核心技术》 代码展示 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Comp

canvas-原生js实现时钟绘图效果

canvas-原生js实现时钟绘图效果 目录 文章目录 前言代码效果查看 前言 本文为canvas实现时钟效果可直接复制使用 代码 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta n

js-基于AudioContext在canvas上显示声音波形

js-基于AudioContext在canvas上显示声音波形 目录 文章目录 前言效果展示代码展示`index.html``Aud.js` 前言 从ES7后开始启用AudioContex常用API是:createScriptProcessor, onaudioprocess, getChannelData注意:onaudioprocess已经废弃,开始改用Analyse

【Canvas与纹饰】环形小蜜蜂纹饰

【成图】 【代码】 <!DOCTYPE html><html lang="utf-8"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><head><title>环形小蜜蜂纹饰</title><style type="text/css">.centerlize{margin:0 auto;

Web前端 lucky-canvas【大转盘 九宫格 老虎机】抽奖插件(适用JS/TS、Vue、React、微信小程序、Uniapp和Taro)

Web前端 lucky-canvas 抽奖插件(JS/TS、Vue、React、微信小程序、Uniapp和Taro) 基于 JS + Canvas 实现的【大转盘 & 九宫格 & 老虎机】抽奖,致力于为 WEB 前端提供一个功能强大且专业可靠的营销组件,只需要通过简单配置即可实现自由化定制,帮助你快速的完成产品需求 自由配置 奖品 / 文字 / 图片 / 颜色 / 按钮均可自由配置;支持同步