123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- import { _decorator, Component, Node, Font, BitmapFont, Vec2, ImageAsset, Texture2D, Asset, RenderTexture, MeshRenderer, Material, Vec3, Size, utils, geometry, EventHandler, Color, ccenum, isValid } from 'cc';
- const { ccclass, property, executeInEditMode } = _decorator;
- export enum FontFilter {
- None,
- Blur,
- Gray,
- }
- ccenum(FontFilter)
- export const BASELINE_RATIO = 0.26;
- const MAX_SIZE = 2048;
- @ccclass('Label3D')
- @executeInEditMode
- export class Label3D extends Component {
- /**
- * 显示文字
- * Arial,serif, 仿宋、黑体、华文彩云、华文细黑、华文新魏、华文行楷、华文中宋、楷体、隶书、幼圆
- */
- @property
- private _font: string = "serif";
- @property({ displayOrder: 0, displayName: "字体" })
- get font() {
- return this._font;
- }
- set font(val) {
- if (this._font === val) {
- return;
- }
- this._font = val;
- if (this._font.length <= 0) {
- this._font = "serif";
- }
- this.updateRenderData();
- }
- /**
- * 显示文字
- */
- @property
- private _string: string = "";
- @property({ displayOrder: 1, multiline: true, displayName: "文字" })
- get string() {
- return this._string;
- }
- set string(val) {
- if (val === null || val === undefined) {
- val = '';
- } else {
- val = val.toString();
- }
- if (this._string === val) {
- return;
- }
- this._string = val;
- this.updateRenderData();
- }
- private _fontSize: number = 50;
- @property
- private _color: Color = new Color();
- @property({ displayOrder: 2, displayName: "字体颜色" })
- get color() {
- return this._color;
- }
- set color(val) {
- this._color = val;
- this.updateRenderData();
- }
- @property
- private _lineHeight: number = 0;
- @property({ displayOrder: 3, displayName: "行高" })
- get lineHeight() {
- return this._lineHeight;
- }
- set lineHeight(val) {
- this._lineHeight = val;
- this.updateRenderData();
- }
- @property
- _strokeWidth: number = 2;
- @property({ displayOrder: 4, displayName: "描边宽度" })
- get strokeWidth() {
- return this._strokeWidth;
- }
- set strokeWidth(val) {
- this._strokeWidth = val;
- if (this._strokeWidth < 0) {
- this._strokeWidth = 0;
- }
- this.updateRenderData();
- }
- @property
- private _strokeColor: Color = new Color();
- @property({ displayOrder: 5, displayName: "描边颜色" })
- get strokeColor() {
- return this._strokeColor;
- }
- set strokeColor(val) {
- this._strokeColor = val;
- this.updateRenderData();
- }
- @property
- _shadowSize: number = 0;
- @property({ displayOrder: 6, displayName: "阴影大小" })
- get shadowSize() {
- return this._shadowSize;
- }
- set shadowSize(val) {
- this._shadowSize = val;
- if (this._shadowSize < 0) {
- this._shadowSize = 0;
- }
- this.updateRenderData();
- }
- @property
- private _shadowColor: Color = new Color();
- @property({ displayOrder: 7, displayName: "阴影颜色" })
- get shadowColor() {
- return this._shadowColor;
- }
- set shadowColor(val) {
- this._shadowColor = val;
- this.updateRenderData();
- }
- @property
- private _filter: number = FontFilter.None;
- @property({ type: FontFilter, displayOrder: 8, displayName: "滤镜" })
- get filter() {
- return this._filter;
- }
- set filter(val) {
- this._filter = val;
- this.updateRenderData();
- }
- @property
- private _blurLenght: number = 5;
- @property({ displayOrder: 9, displayName: "模糊强度", range: [0, 10], step: 0.01, slide: true, visible: function (this) { return this._filter === FontFilter.Blur ? true : false; } })
- get blurLenght() {
- return this._blurLenght;
- }
- set blurLenght(val) {
- this._blurLenght = val;
- this.updateRenderData();
- }
- @property
- private _grayLenght: number = 5;
- @property({ displayOrder: 10, displayName: "置灰强度", range: [0, 100], step: 1, slide: true, visible: function (this) { return this._filter === FontFilter.Gray ? true : false; } })
- get grayLenght() {
- return this._grayLenght;
- }
- set grayLenght(val) {
- this._grayLenght = val;
- this.updateRenderData();
- }
- private _splitStrings: string[] = [];
- private _context: CanvasRenderingContext2D = null!
- private _canvas: HTMLCanvasElement = null!;
- private _texture: Texture2D = null!;
- private _meshRender: MeshRenderer = null!;
- private _canvasSize: Size = new Size();
- /**
- * mesh uvs
- */
- private _uvs: number[] = [];
- /**
- * mesh 顶点坐标
- */
- private _positions: number[] = [];
- private _startPosition: Vec2 = new Vec2();
- onLoad() {
- this.initRenderingContext();
- this.initMeshRender();
- this.initTexture2D();
- this.updateRenderData();
- }
- onEnable() {
- }
- start() {
- }
- private initRenderingContext(): void {
- this._canvas = document.createElement('canvas');
- this._context = this._canvas.getContext('2d');
- }
- private initMeshRender(): void {
- this._meshRender = this.node.getComponent(MeshRenderer)!;
- if (!this._meshRender) {
- this._meshRender = this.node.addComponent(MeshRenderer);
- }
- }
- private initTexture2D(): void {
- this._texture = new Texture2D();
- let image: ImageAsset = new ImageAsset(this._canvas);
- this._texture.image = image;
- }
- /**
- * 刷新渲染
- */
- private updateRenderData(): void {
- this.resetRenderData();
- this.updateProperties();
- this.updateTexture();
- this.updateRenderMesh();
- this.updateMaterial();
- }
- private updateProperties(): void {
- //设置canvas 的宽和高
- this._splitStrings = this._string.split("\\n");
- for (let i = 0; i < this._splitStrings.length; i++) {
- //获取文本的宽度
- let len: number = this._context.measureText(this._splitStrings[i]).width;
- if (len > this._canvasSize.width) {
- this._canvasSize.width = len;
- }
- }
- this._canvasSize.height = this._splitStrings.length * this.getLineHeight() + BASELINE_RATIO * this.getLineHeight();
- this._canvasSize.width = Math.min(this._canvasSize.width, MAX_SIZE);
- this._canvasSize.height = Math.min(this._canvasSize.height, MAX_SIZE);
- if (this._canvas.width != this._canvasSize.width) {
- this._canvas.width = this._canvasSize.width;
- }
- if (this._canvas.height != this._canvasSize.height) {
- this._canvas.height = this._canvasSize.height;
- }
- //设置字体样式
- this._context.font = "bold " + this._fontSize + "px " + "Arial";
- // this._context.
- this._context.lineWidth = this._strokeWidth;
- this._context.fillStyle = "#" + this._color.toHEX();
- this._context.strokeStyle = "#" + this._strokeColor.toHEX();
- this._context.shadowBlur = this._shadowSize;
- this._context.shadowColor = "#" + this._shadowColor.toHEX();
- if (this._filter === FontFilter.Blur) {
- this._context.filter = "blur(" + this._blurLenght + "px)";
- } else if (this._filter === FontFilter.Gray) {
- this._context.filter = "grayscale(" + this._grayLenght + "%)";
- } else {
- this._context.filter = "none";
- }
- }
- private updateTexture(): void {
- if (!this._context || !this._canvas) return;
- this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);
- let textPosX: number = 0;
- let textPosY: number = 0;
- for (let i = 0; i < this._splitStrings.length; i++) {
- textPosY = this._startPosition.y + (i + 1) * this.getLineHeight();
- let len: number = this._context.measureText(this._splitStrings[i]).width;
- textPosX = (this._canvas.width - len) / 2;
- this._context.fillText(this._splitStrings[i], textPosX, textPosY);
- if (this._strokeWidth > 0) {
- this._context.strokeText(this._splitStrings[i], textPosX, textPosY);
- }
- }
- let uploadAgain: boolean = this._canvas.width !== 0 && this._canvas.height !== 0;
- if (uploadAgain) {
- this._texture.reset({
- width: this._canvas.width,
- height: this._canvas.height,
- mipmapLevel: 1,
- });
- this._texture.uploadData(this._canvas);
- this._texture.setWrapMode(RenderTexture.WrapMode.CLAMP_TO_EDGE, RenderTexture.WrapMode.CLAMP_TO_EDGE);
- }
- }
- private updateMaterial(): void {
- if (!this._texture) return;
- if (!this._meshRender) return;
- let material: Material = this._meshRender.getMaterialInstance(0)!;
- material.setProperty("mainTexture", this._texture);
- }
- /**
- * 根据canvas的实际宽高
- * 动态的调整mesh的坐标
- */
- private updateRenderMesh(): void {
- let rate: number = this._canvas.width / this._canvas.height;
- this._positions = [];
- this._uvs = [];
- this._positions.push(-0.5 * rate, -0.5, 0); this._uvs.push(0, 1);
- this._positions.push(0.5 * rate, -0.5, 0); this._uvs.push(1, 1);
- this._positions.push(-0.5 * rate, 0.5, 0); this._uvs.push(0, 0);
- this._positions.push(-0.5 * rate, 0.5, 0); this._uvs.push(0, 0);
- this._positions.push(0.5 * rate, -0.5, 0); this._uvs.push(1, 1);
- this._positions.push(0.5 * rate, 0.5, 0); this._uvs.push(1, 0);
- this._meshRender.mesh = utils.MeshUtils.createMesh({
- positions: this._positions,
- uvs: this._uvs,
- minPos: { x: -0.5 * rate, y: -0.5, z: 0 },
- maxPos: { x: 0.5 * rate, y: 0.5, z: 0 }
- });
- this._meshRender.model?.updateWorldBound();
- }
- /**
- * 获取行高
- */
- private getLineHeight(): number {
- return this._fontSize + this._lineHeight; //行高 -暂时写成40
- }
- private resetRenderData(): void {
- this._canvasSize.width = 0;
- this._canvasSize.height = 0;
- //设置字体样式
- this._context.font = "bold " + this._fontSize + "px " + "Arial";
- this._context.lineWidth = this._strokeWidth;
- this._context.fillStyle = "#" + this._color.toHEX();
- this._context.strokeStyle = "#" + this._strokeColor.toHEX();
- this._context.shadowBlur = this._shadowSize;
- this._context.shadowColor = "#" + this._shadowColor.toHEX();
- if (this._filter === FontFilter.Blur) {
- this._context.filter = "blur(" + this._blurLenght + "px)";
- } else if (this._filter === FontFilter.Gray) {
- this._context.filter = "grayscale(" + this._grayLenght + "%)";
- } else {
- this._context.filter = "none";
- }
- }
- update(deltaTime: number) {
- }
- onDestroy() {
- this._canvas = null;
- this._context = null;
- this._meshRender = null!;
- }
- }
|