Skip to content

Instantly share code, notes, and snippets.

@dxball
Created July 14, 2022 06:52
Show Gist options
  • Save dxball/5a7c6849cadac5a02d778c4eda30fd42 to your computer and use it in GitHub Desktop.
Save dxball/5a7c6849cadac5a02d778c4eda30fd42 to your computer and use it in GitHub Desktop.
Cocos 3.X Gradient
import { _decorator, Color, Component, Renderable2D } from "cc";
const {
ccclass,
property,
requireComponent,
disallowMultiple,
executeInEditMode,
} = _decorator;
/**
* 漸層色,支援 Sprite (Simple & Filled), Label (TTF Font)
* @reference https://github.com/ifaswind/cocos-eazax-kit/blob/develop/components/renderers/GradientColor.ts
*/
@ccclass("UIGradientColor")
@requireComponent(Renderable2D)
@disallowMultiple
@executeInEditMode
export class UIGradientColor extends Component {
@property({ visible: true, tooltip: "左下" })
protected _colorLB: Color = Color.RED.clone();
@property({ visible: true, tooltip: "右下" })
protected _colorRB: Color = Color.BLUE.clone();
@property({ visible: true, tooltip: "左上" })
protected _colorLT: Color = Color.YELLOW.clone();
@property({ visible: true, tooltip: "右上" })
protected _colorRT: Color = Color.GREEN.clone();
private _isEnable: boolean = true;
/**
* 取得或設定漸層顏色,[左下、右下、左上、右上]
*/
public get colors(): Readonly<Color>[] {
return [this._colorLB, this._colorRB, this._colorLT, this._colorRT];
}
public set colors(colors: ReadonlyArray<Color>) {
if (colors.length < 4) return;
[this._colorLB, this._colorRB, this._colorLT, this._colorRT] = colors;
this.markForRender();
}
/**
* 左上
*/
public get colorLT(): Readonly<Color> {
return this._colorLT;
}
public set colorLT(color: Color) {
if (this._colorLT.equals(color)) return;
this._colorLT.set(color);
this.markForRender();
}
/**
* 右上
*/
public get colorRT(): Readonly<Color> {
return this._colorRT;
}
public set colorRT(color: Color) {
if (this._colorRT.equals(color)) return;
this._colorRT.set(color);
this.markForRender();
}
/**
* 左下
*/
public get colorLB(): Readonly<Color> {
return this._colorLB;
}
public set colorLB(color: Color) {
if (this._colorLB.equals(color)) return;
this._colorLB.set(color);
this.markForRender();
}
/**
* 右下
*/
public get colorRB(): Readonly<Color> {
return this._colorRB;
}
public set colorRB(color: Color) {
if (this._colorRB.equals(color)) return;
this._colorRB.set(color);
this.markForRender();
}
onEnable() {
this._isEnable = true;
this.replaceFunction();
}
onDisable() {
this._isEnable = false;
this.restoreFunction();
}
onDestroy() {
this._isEnable = false;
this.restoreFunction();
}
resetInEditor() {
this.replaceFunction();
}
/**
* 把 IAssembler2D.updateColor 換成自訂的
*/
protected replaceFunction() {
const renderComp = this.getComponent(Renderable2D);
if (!renderComp) return;
const assembler = renderComp["_assembler"];
if (!assembler) return;
// 這邊取到的 Assembler 是元件共用的,所以設計成先把舊的 updateColor 記錄下來
// 當呼叫 updateColor 時先檢查元件是否有掛上 UIGradientColor,如果沒有的話就呼叫元件本身的
const originalUpdateColor = assembler["_originalUpdateColor"];
if (!originalUpdateColor) {
// 備份原有的 updateColor
assembler["_originalUpdateColor"] = assembler["updateColor"];
// 替换顏色填充 Function
assembler["updateColor"] = UIGradientColor.OnAssemblerUpdateColor;
}
// 标记
this.markForRender();
}
private static OnAssemblerUpdateColor(comp: Renderable2D) {
let gradient = comp.node.getComponent(UIGradientColor);
if (gradient == null) {
const comps = comp.node.components;
for (let i = 0; i < comps.length; i++) {
const tmpComp = comps[i];
if (tmpComp instanceof UIGradientColor) {
gradient = tmpComp;
break;
}
}
}
if (gradient && gradient._isEnable) {
const renderData = comp.renderData;
if (renderData == null) return;
if (renderData.chunk == null) return;
// 取得頂點顏色資料
const vData = renderData.chunk.vb;
if (!vData) return;
let colorOffset = 5; // 偏移
const colors = [
gradient._colorLB,
gradient._colorRB,
gradient._colorLT,
gradient._colorRT,
];
// 設定頂點顏色
for (let i = 0; i < 4; i++, colorOffset += renderData.floatStride) {
vData[colorOffset] = colors[i].r / 255;
vData[colorOffset + 1] = colors[i].g / 255;
vData[colorOffset + 2] = colors[i].b / 255;
vData[colorOffset + 3] = colors[i].a / 255;
}
} else {
const _assembler = comp["_assembler"];
if (_assembler) {
_assembler["_originalUpdateColor"]?.(comp);
}
}
}
/**
* 還原
*/
protected restoreFunction() {
const renderComp = this.getComponent(Renderable2D);
if (!renderComp) return;
const assembler = renderComp["_assembler"];
if (!assembler) return;
renderComp.destroyRenderData();
renderComp["_flushAssembler"]?.();
this.markForRender();
}
/**
* 更新顏色
*/
public markForRender() {
const renderComp = this.getComponent(Renderable2D);
if (!renderComp) return;
renderComp["_updateColor"]?.();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment