当前位置:网站首页>快应用JS自定义月相变化效果
快应用JS自定义月相变化效果
2022-07-27 09:20:00 【Mr_Tony】
一、前言
工作中要制作一个月相变化的效果,数据接口来源于:
http://gaofen.mlogcn.com/documentation/6/04
制作出一个跟随时间变化的月相效果图:整体效果如下:
在网站上附带了月相图片资源36张。但是受限于快应用,然后图片资源过大,所以需要进行效果的自定义,这里将相关代码进行记录。整体代码思路是用一个满月的图片做为背景,上面用两个三阶贝塞尔曲线画阴影。
二、相关月相js代码
canvas.js
//绘制一个圆
//其实背景整体是一个方形,只是外面的布局设置了圆角50%
//背景方形为150px。中心为(75,75)。光晕为15px。所以得出以下效果
//右侧半圆阴影ctx.bezierCurveTo(155, 15, 155, 132, 75, 135);
//处于中间一条线时候,该线看不见ctx.bezierCurveTo(75, 15, 75, 132, 75, 135);
//左侧半圆阴影ctx.bezierCurveTo(-5, 15, -5, 132, 75, 135);
//结论,中间值为75,当两个坐标的x点为75时候,线消失。距离左右的值为80,左侧极点为-5,右侧极点为155
//top、bottom的位置在月相变化过程中不变,为固定值
//绘制思路,整个月球阴影由两部分绘制,一部分是左侧的阴影,一侧是右侧的阴影,整个阴影变化时候只有X坐标会移动
//当 X > 75 时候向右移动,X < 75 时候 向左移动
const totalPercent = 35 // 分为35份,该数量是从http://gaofen.mlogcn.com/documentation/6/04 接口中的月相数量获取,
const centerPercent = 18 // 该值为新月,此时月影覆盖整个月亮 0 为 满月状态
const operationIn = "destination-in"
const operationOut = "destination-out"
let canvasSize = 600
let centerX = canvasSize / 2 //中心坐标 canvasSize / 2
let haloWidth = canvasSize / 10 //光晕宽度
let moonRadius = (canvasSize - haloWidth * 2) / 2 //月亮半径(不带光晕) (canvasSize - haloWidth * 2) / 2
let minX = -30 // 左侧极点 为 totalX 的左端
let maxX = 690 //右侧极点 为 totalX 的右端
let totalX = maxX - centerX //左右两端的距离 //左右两侧的极点距离为 月球宽度 + 光晕宽度 = canvasSize + haloWidth
let moonBottom = canvasSize - haloWidth //月亮底部位置 canvasSize - haloWidth
let moonOffsetBottom = moonBottom - 1 //偏移位置
// controlPointX1 = minX
// controlPointX2 = tempPercent * step + centerX
let controlPointX1 = centerX //控制点1的X坐标 // 控制左侧 -5 到 75 之间移动
let controlPointX2 = centerX //控制点2的X坐标 // 控制右侧 75 - 155 之间移动
let compositeOperation = operationIn //图像合成模式
let step = totalX / 8
//ctx: 画布操作对象
//size: 画布大小
//precent: 月相进度
function drawCircle(ctx, size, precent) {
precent = precent || 0
canvasSize = size
updateSize()
ctx.clearRect(0, 0, size, size);//清除画布,重新绘制阴影
getTranslateX(precent)
drawMoonShadow(ctx)
}
//当外部更改月球大小时候,此处需要重新计算各种数据
function updateSize() {
centerX = canvasSize / 2 //中心坐标 canvasSize / 2
haloWidth = canvasSize / 10 //光晕宽度
let centerOffest = (canvasSize + haloWidth) / 2 - centerX //贝塞尔曲线的中间值
moonRadius = (canvasSize - haloWidth * 2) / 2 //月亮半径(不带光晕) (canvasSize - haloWidth * 2) / 2
minX = -centerOffest // 左侧极点 为 totalX 的左端
maxX = canvasSize + centerOffest //右侧极点 为 totalX 的右端
totalX = maxX - centerX //左右两端的距离 //左右两侧的极点距离为 月球宽度 + 光晕宽度 = canvasSize + haloWidth
moonBottom = canvasSize - haloWidth //月亮底部位置 canvasSize - haloWidth
moonOffsetBottom = moonBottom - 1 //偏移位置
// controlPointX1 = minX
// controlPointX2 = tempPercent * step + centerX
controlPointX1 = centerX //控制点1的X坐标 // 控制左侧 -5 到 75 之间移动
controlPointX2 = centerX //控制点2的X坐标 // 控制右侧 75 - 155 之间移动
}
//绘制月亮MoonShadow
function drawMoonShadow(ctx) {
drawLayer(ctx)
ctx.save()
drawMask(ctx)
}
//绘制阴影
function drawMask(ctx) {
// 正常绘制第一个矩形, 设置图形合成模式
ctx.globalCompositeOperation = compositeOperation
ctx.beginPath()
ctx.fillStyle = '#000000';
ctx.globalAlpha = 1;
ctx.moveTo(centerX, haloWidth);//偏移至不带光晕的月亮X轴中间, 绘制左侧半圆
ctx.bezierCurveTo(controlPointX1, haloWidth, controlPointX1, moonOffsetBottom, centerX, moonBottom);
ctx.moveTo(centerX, haloWidth);//偏移至不带光晕的月亮X轴中间, 绘制右侧半圆
ctx.bezierCurveTo(controlPointX2, haloWidth, controlPointX2, moonOffsetBottom, centerX, moonBottom);
ctx.fill();
ctx.closePath();
}
//设置背景,画一个圆
function drawLayer(ctx) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.fillStyle = '#000000';
ctx.globalAlpha = 0.7;
ctx.arc(centerX, centerX, moonRadius, 0, Math.PI * 2, false);
ctx.fill();
ctx.closePath();
}
//通过进度获取,月相的移动距离
function getTranslateX(percent) {
let x = 0
if (percent == 0) {
//满月, 由于合成模式的关系,这里操作有点反常识
compositeOperation = operationIn
controlPointX1 = centerX
controlPointX2 = centerX
} else if (0 < percent && percent < 9) {
// 蛾眉月
//计算controlPointX1的值,该值从-5 至 75之间移动,该位置有 8 个变化,所以step为 10
compositeOperation = operationOut
controlPointX1 = percent * step + minX
controlPointX2 = maxX
} else if (percent == 9) {
//上弦月
//此时需要更改图片合成模式
compositeOperation = operationOut
controlPointX1 = centerX
controlPointX2 = maxX
} else if (9 < percent && percent < 18) {
// 盈凸月
//计算controlPointX1的值,该值从75 至 155之间移动,该位置有 8 个变化,所以step为 10
compositeOperation = operationIn
let tempPercent = percent - 9
controlPointX1 = minX
controlPointX2 = tempPercent * step + centerX
} else if (percent == 18) {
//新月
compositeOperation = operationIn
controlPointX1 = minX
controlPointX2 = maxX
} else if (18 < percent && percent < 27) {
// 亏凸月
//计算controlPointX1的值,该值从-5 至 75之间移动,该位置有 8 个变化,所以step为 10
compositeOperation = operationIn
let tempPercent = percent - 18
controlPointX1 = tempPercent * step + minX
controlPointX2 = maxX
} else if (percent == 27) {
//下弦月
compositeOperation = operationOut
controlPointX1 = minX
controlPointX2 = centerX
} else if (27 < percent && percent <= 35) {
// 残月
//计算controlPointX1的值,该值从75 至 155之间移动,该位置有 8 个变化,所以step为 10
let tempPercent = percent - 27
compositeOperation = operationOut
controlPointX1 = minX
controlPointX2 = tempPercent * step + centerX
}
}
export default {
drawCircle
}
使用方式
<stack class="moon-stack">
<image class="moon-img" src="../../assets/images/moon_0.png"> </image>
<canvas class="moon-mask" id="moon-mask"> </canvas>
</stack>
<style lang="less"> .moon-stack {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 6px ; .moon-img {
width: 300px;
height: 300px;
}
}
.moon-mask {
width: 300px;
height: 300px;
border-radius: 50%;
}
</style>
import moonCanvas from "./canvas"
const moonSize = 300 * 2//月球大小
export default {
show(){
const canvas = this.$element('moon-mask')
ctx = canvas.getContext('2d')
updateMoonProgress(ctx, 0)//默认渲染成满月状态
}
}
//刷新画布
function updateMoonProgress(ctx, progress) {
moonCanvas.drawCircle(ctx, moonSize, progress)
}
边栏推荐
- Day 6 of learning C language
- 基于restful页面数据交互
- Day 8 of learning C language
- HBuilder 微信小程序中运行uni-app项目
- js call和apply
- ES6 new - array part
- The whole process of principle, simulation and verification of breath lamp controlled by FPGA keys
- Five kinds of 3D attention/transformer finishing (a-scn, point attention, CAA, offset attention, point transformer)
- Size limit display of pictures
- STL container - basic operation of queue and deque
猜你喜欢

Mangodb simple to use

Interviewer: what is scaffolding? Why do you need scaffolding? What are the commonly used scaffolds?

1344. Included angle of clock pointer

ES6 new - array part

Restful

基于 FPGA 按键控制呼吸灯原理、仿真及验证全过程

js call和apply

BGP联邦实验

Is the operation of assigning values to int variables atomic?

音乐体验天花板!14个网易云音乐的情感化设计细节
随机推荐
Restful
Read the paper snunet CD: a densely connected Siamese network for change detection of VHR images
Specific methods and steps for Rockwell AB PLC to establish communication with PLC through rslinx classic
【微信小程序】农历公历互相转换
IDL calls 6S atmospheric correction
npm和yarn 更新依赖包
ArkUI中的显式动画
The third day of learning C language
STL container -- Application of set set
swagger-editor
全排列递归思路整理
Interviewer: what is scaffolding? Why do you need scaffolding? What are the commonly used scaffolds?
【云原生之kubernetes实战】在kubernetes集群下部署Rainbond平台
What if the parameters in QT are structs or custom classes when sending signals?
The whole process of principle, simulation and verification of breath lamp controlled by FPGA keys
Function anti chattering throttling
BGP的社团属性
基于 FPGA 按键控制呼吸灯原理、仿真及验证全过程
MySQL transaction
面试官:什么是脚手架?为什么需要脚手架?常用的脚手架有哪些?