这里是本系列的第二讲,上一讲不知道大家自己试过了没,虽然整体效果差强人意但是并不算完美。本讲我们先用那个canvas2D把上一讲的效果实现一遍。
还是先讲原理,动手能力强的童鞋可以看了原理自己去鼓捣。
设有三角函数 y = Asin(ωx+φ):
第一步,我们要先利用高中的三角函数知识来计算出旗帜飘动(类似简谐运动)的第一帧 (设初相φ=0) 的曲线,
假设我们有一张图片,宽高分别为W、H,该图片包含n个周期,最大振幅为A,可得波长 λ = W / n ,ω = 2π / λ ,即可得出我们的三角函数:y = A · sin( 2π / λ · x )
第二步,在W次循环中(逐像素),使用drawImage函数 来画出宽度1,高度H的图片。
第三步,根据x轴偏移量修改振幅的系数。据本人“观察”,现实生活中,旗子的尾部摆动幅度更大。为了做到这个效果,修改三角函数为 y = A · ( x ÷ W ) · sin( 2π / λ · x )
(我使用的振幅变化是线性递增的,很遗憾本人的物理水平不咋地,不知道理想状况下振幅应该是怎样的变化关系,不过看的差不多就行了,)
第四步,给我们的第一帧画面添加时间轴,将曲线随时间往右平移来呈现动画效果,设频率为f,t 为上一帧到这帧的间隔,波速 v = λ · f ,修改我们的函数: y = A · ( x ÷ W ) · sin( 2π / λ · ( x - t · v ))
第五步,由于运动的坐标原点是左上角,所以超出的部分会被裁剪。我们先创建一个宽高分别为W和H+2 * A的 画布,然后将函数往下移动,是其正好被包裹,因为canvas坐标系与数学中的直角坐标系方向相反,所以要加上A,得到最终的函数: y = A · ( x ÷ W ) · sin( 2π / λ · ( x - t · v )) + A
以上就是我们主要的步骤,角度需要换成弧度制。有些人可能会疑问,上一讲不是需要做一次反向平衡来防止旗帜的最左边也波动起来吗?最左边x轴坐标为0,代入我们的函数,得到y = 0,也就是振幅为0,所以就可以免去了上一讲的步骤,当然,如果你觉得我的“观察”没有啥依据,还是按照标准的三角函数( y = A · sin( 2π / T · ( x - t · v )) )来做,那么你需要在每一帧中,先计算出x=0时候的振幅,记为 y0 = A · sin( 2π / T · ( -t · v ÷ T )),然后将图形向上或者向下移动-y0 ,修改我们的函数: y = A · sin( 2π / T · ( x - t · v )) + A + y0 。
ok,辛苦你看到现在,代码量也不大,我一次贴出来吧。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>旗帜飘飘</title>
<style>
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
}
body {
position: relative;
background: lightgrey;
}
#flagCanvas {
position: absolute;
top: 50%;
left: 50%;
transform-origin: center;
transform: translate3d(-50%, -50%, 0);
}
</style>
</head>
<body>
<canvas id="flagCanvas"></canvas>
<script>
var canvas = document.getElementById('flagCanvas')
var ctx = canvas.getContext('2d')
var IMG_MAX_WIDTH = 600
var IMG_MAX_HEIGHT = 600
var imgWidth, imgHeight
var image = new Image()
image.src = '../img/flag.jpg'
var amplitude = 30 // 振幅
var period = 2 // 周期数
var frequency = 1 // 频率
var wavelength // 波长
var v // 波速
var cftX // x系数
var cftA // 振幅系数
image.onload = function (ev) {
imgWidth = Math.floor(image.width)
imgHeight = Math.floor(image.height)
var canvas = document.getElementById('flagCanvas')
var scale = 1
if (imgWidth > IMG_MAX_WIDTH) {
scale = IMG_MAX_WIDTH / imgWidth
}
if (imgHeight > IMG_MAX_HEIGHT) {
scale = scale * IMG_MAX_HEIGHT / imgHeight
}
canvasWidth = imgWidth
canvasHeight = imgHeight + amplitude * 2
canvas.width = canvasWidth
canvas.height = canvasHeight
canvas.style.transform = 'translate3d(-50%,-50%,0) scale(' + scale + ')'
wavelength = imgWidth / period
cftX = 2 * Math.PI / wavelength
cftA = amplitude / imgWidth
v = wavelength * frequency
tick()
}
var fps = 70 // 每秒帧数
var interval = 1000 / fps // 连续帧之间间隔(理论)
var stop = false // 停止动画
var timeNow = Date.now() // 当前时间
var timeLast = timeNow // 上一帧时间
var delta = 0 // 连续帧之间间隔(实际)
var distance = 0
var tick = function () {
if (stop) return false
timeNow = Date.now()
delta = timeNow - timeLast
if (delta > interval) {
timeLast = timeNow
distance += (delta / 1000 * v)
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
for (var x = 0; x < imgWidth; x++) {
var y = cftA * x * Math.sin(cftX * (x - distance)) + amplitude
ctx.drawImage(image, x, 0, 1, imgHeight, x, y, 1, imgHeight)
}
}
requestAnimationFrame(tick)
}
</script>
</body>
</html>
效果如下:
还是熟悉的味道,不过比较一下上一讲的效果图,你会发现无论是性能还是呈现效果都有所改进
说一下几个关键点:
① 第70行有一段代码 canvas.style.transform = 'translate3d(-50%,-50%,0) scale(' + scale + ')' 。因为如果图片太大需要进行缩放,但是通过canvas2D来处理会性能并不会太高,因为其并没有用到gpu来渲染,所以我选择了translate3d来强制开启硬件加速;
② drawImage方法可以传入9个参数,这是函数的原型: void CanvasRenderingContext2D.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
似乎基本要主意的就这些了,这次就那么多,下一讲在这个基础上给旗帜添加高光效果。
原网址: 访问
创建于: 2018-10-13 16:40:37
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论