/*! * VERSION: 1.6.3 * DATE: 2019-09-13 * https://leon-sans.com * * @license Copyright (c) 2019, Jongmin Kim. All rights reserved. **/ import { Dispatcher } from './core/dispatcher.js'; import { MIN_FONT_WEIGHT, MAX_FONT_WEIGHT, shuffle } from './core/util.js'; import { Lines } from './draw/canvas/lines.js'; import { Points } from './draw/canvas/points.js'; import { Grids } from './draw/canvas/grids.js'; import { Wave } from './draw/canvas/wave.js'; import { Pattern } from './draw/canvas/pattern.js'; import { Color } from './draw/canvas/color.js'; import { Colorful } from './draw/canvas/colorful.js'; import { PixiLines } from './draw/pixi/lines.js'; import { PixiColor } from './draw/pixi/color.js'; import { Model } from './core/model.js'; export default class LeonSans extends Dispatcher { constructor({ text = '', size = 500, weight = MIN_FONT_WEIGHT, color = ['#000000'], colorful = ['#c5d73f', '#9d529c', '#49a9db', '#fec330', '#5eb96e', '#fc5356', '#f38f31'], tracking = 0, leading = 0, align = 'left', pathGap = 0.5, amplitude = 0.5, width = 0, breakWord = false, fps = 30, isPath = false, isWave = false, } = {}) { super(); this.size_ = size; this.weight_ = weight; this.color_ = color; this.colorful_ = shuffle(colorful); this.tracking_ = tracking; this.leading_ = leading; this.pathGap_ = pathGap; this.amplitude_ = amplitude; this.width_ = width; this.breakWord_ = breakWord; this.fps_ = fps; this.fpsTime_ = 1000 / this.fps_; this.isPath_ = isPath; this.isWave_ = isWave; this.model = new Model(); this.str_ = null; this.time_ = null; this.isFps_ = false; this.isForceRander_ = false; this.updateID_ = 0; this.dPathsID_ = null; this.pPathsID_ = null; this.wPathsID_ = null; this.guideID_ = null; this.text = text; this.model.align = align; } on(event, callback) { super.on(event, callback); this.update(); } off(event, callback) { super.off(event, callback); } get text() { return this.str_; } set text(str) { if (this.str_ == str) return; this.str_ = str; this.update(); } get size() { return this.size_; } set size(v) { if (this.size_ == v) return; this.size_ = v; this.update(); this.isForceRander_ = true; } get weight() { return this.weight_; } set weight(v) { if (v < MIN_FONT_WEIGHT) { v = MIN_FONT_WEIGHT; } else if (v > MAX_FONT_WEIGHT) { v = MAX_FONT_WEIGHT; } if (this.weight_ == v) return; this.weight_ = v; this.update(); this.isForceRander_ = true; } get color() { return this.color_; } set color(v) { if (this.color_ == v) return; this.color_ = v; } get tracking() { return this.tracking_; } set tracking(v) { if (this.tracking_ == v) return; this.tracking_ = v; this.update(); this.isForceRander_ = true; } get leading() { return this.leading_; } set leading(v) { if (this.leading_ == v) return; this.leading_ = v; this.update(); this.isForceRander_ = true; } get align() { return this.model.align; } set align(v) { if (this.model.align != v) { this.model.align = v; this.updateID_++; this.updateSignal(); } } get pathGap() { return this.pathGap_; } set pathGap(v) { if (this.pathGap_ != v) { this.pathGap_ = v; this.updatePatternPaths(true); this.updateWavePaths(true); this.isForceRander_ = true; } } get amplitude() { return this.amplitude_; } set amplitude(v) { this.amplitude_ = v; } get rect() { return this.model.rect; } set maxWidth(v) { if (this.width_ == v) return; this.width_ = v; this.update(); } get maxWidth() { return this.width_; } set breakWord(v) { if (this.breakWord_ == v) return; this.breakWord_ = v; this.update(); } get breakWord() { return this.breakWord_; } get isPath() { return this.isPath_; } set isPath(v) { this.isPath_ = v; this.updatePatternPaths(true); } get isWave() { return this.isWave_; } set isWave(v) { this.isWave_ = v; this.updateWavePaths(true); } get fps() { return this.fps_; } set fps(v) { this.fps_ = v; this.fpsTime_ = 1000 / this.fps_; } get lineWidth() { return this.model.lineWidth; } get scale() { return this.model.scale; } get drawing() { return this.model.drawing; } get data() { return this.model.data; } get paths() { return this.model.paths; } get drawingPaths() { return this.model.drawingPaths; } get wavePaths() { return this.model.wavePaths; } position(x = 0, y = 0) { if (this.model.position(x, y)) { this.updateID_++; this.updateSignal(); } } update() { this.updateID_++; this.model.update( this.str_, this.width_, this.breakWord_, this.weight_, this.size_, this.tracking_, this.leading_ ); if (this.isPath_ || this.isWave_) { this.updatePatternPaths(); this.updateWavePaths(); } else { this.updateSignal(); } } updateGuide() { if (this.guideID_ != this.updateID_) { this.guideID_ = this.updateID_; this.model.updateGuide(); } } /** * Update paths for drawing in WebGL (PIXI.js). It's very expensive, only call when it needs. */ updateDrawingPaths() { if (this.dPathsID_ != this.updateID_) { this.dPathsID_ = this.updateID_; this.model.updateDrawingPaths(); } } /** * Update paths for pattern * @param {boolean} force - Force execution */ updatePatternPaths(force) { if (this.isPath_ && (force || this.pPathsID_ != this.updateID_)) { this.pPathsID_ = this.updateID_; this.model.updatePatternPaths(this.pathGap_); this.isForceRander_ = true; this.updateSignal(); } } /** * Update paths for wave effect * @param {boolean} force - Force execution */ updateWavePaths(force) { if (this.isWave_ && (force || this.wPathsID_ != this.updateID_)) { this.wPathsID_ = this.updateID_; this.model.updateWavePaths(this.pathGap_); this.isForceRander_ = true; this.updateSignal(); } } updateSignal() { this.model.updateLinesForRect(); this.model.updatePathsForRect(); this.dispatch('update', this.model); } reset() { this.size_ = 500; this.weight_ = MIN_FONT_WEIGHT; this.color_ = ['#000000']; this.tracking_ = 0; this.leading_ = 0; this.pathGap_ = 0.5; this.amplitude_ = 0.5; this.width_ = 0; this.breakWord_ = false; this.fps_ = 30; this.fpsTime_ = 1000 / this.fps_; this.isPath_ = false; this.isWave_ = false; this.str_ = null; this.time_ = null; this.isFps_ = false; this.isForceRander_ = false; this.updateID_ = 0; this.dPathsID_ = null; this.pPathsID_ = null; this.wPathsID_ = null; this.guideID_ = null; this.model.reset(); } dispose() { this.reset(); this.model = null; } /** * Draw text in WebGL with PIXI.js * @param {PIXI.Graphics} graphics */ drawPixi(graphics) { const total = this.model.data.length; let i, d, color; for (i = 0; i < total; i++) { d = this.model.data[i]; color = PixiColor(i, d, this.color_); PixiLines(graphics, d, this.lineWidth, color); } } /** * Draw text in the Canvas element. * @param {CanvasRenderingContext2D} ctx */ draw(ctx) { ctx.lineWidth = this.lineWidth; const total = this.model.data.length; let i, d; for (i = 0; i < total; i++) { d = this.model.data[i]; Color(ctx, i, d, this.color_); Lines(ctx, d); } } /** * Draw the colorful effect. * @param {CanvasRenderingContext2D} ctx */ drawColorful(ctx) { ctx.lineWidth = this.lineWidth; Colorful(ctx, this.model, this.colorful_); } /** * Draw the wave effect. * @param {CanvasRenderingContext2D} ctx * @param {DOMHighResTimeStamp} t time stemp from requestAnimationFrame() */ wave(ctx, t) { ctx.lineWidth = this.lineWidth; if (t) { if (!this.time_) this.time_ = t; const p = t - this.time_; if (p > this.fpsTime_ || this.isForceRander_) { this.time_ = t; this.isFps_ = true; } else { this.isFps_ = false; } } this.isForceRander_ = false; const total = this.model.data.length; let i, d; for (i = 0; i < total; i++) { d = this.model.data[i]; Color(ctx, i, d, this.color_); Wave(ctx, d, this.model.scale, this.amplitude_, this.weight_, this.isFps_); } } /** * Draw rectangle shapes at each path point. * @param {CanvasRenderingContext2D} ctx * @param {number} w pattern width * @param {number} h pattern height */ pattern(ctx, w, h) { const tw = w * this.model.scale; const th = h * this.model.scale; const total = this.model.data.length; let i, d; for (i = 0; i < total; i++) { d = this.model.data[i]; Pattern(ctx, d, tw, th); } } /** * Draw grid for each type. * @param {CanvasRenderingContext2D} ctx */ grid(ctx) { this.updateGuide(); const total = this.model.data.length; let i, d; for (i = 0; i < total; i++) { d = this.model.data[i]; Grids(ctx, d); } } /** * Draw circles at each drawing point and lines for each type. * @param {CanvasRenderingContext2D} ctx */ point(ctx) { const total = this.model.data.length; let i, d; for (i = 0; i < total; i++) { d = this.model.data[i]; Points(ctx, d); } } /** * Draw outline box for the text. * @param {CanvasRenderingContext2D} ctx * @private */ box(ctx) { ctx.lineWidth = 1; ctx.beginPath(); ctx.strokeStyle = "#0b90dc"; ctx.rect(this.model.rect.x, this.model.rect.y, this.model.rect.w, this.model.rect.h); ctx.stroke(); } }