Leon Sans is a geometric sans-serif typeface made with code in 2019 by Jongmin Kim.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

528 lines
11 KiB

/*!
* 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();
}
}