快应用JS自定义月相变化效果

一、前言

工作中要制作一个月相变化的效果,数据接口来源于:
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)
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值