morthics
v0.2.4
Published
Mothics is a typescript framework designed to work with WebGL
Downloads
4
Maintainers
Readme
Morthics
Morthics is a typescript framework designed to work with WebGL. The framework is based on a component-based approach that makes development faster and easier. Morthics makes it possible to manage the program at a low level, and also ensures high speed of the program due to its simplicity, webgl and many optimizations
Installation
npm i morthics
Usage
main.ts
import { Application } from 'morthics'
;(async () => {
const app = new Application(
{ resizeTo: window, backgroundColor: '#242424' },
Main
)
await app.load
document.body.appendChild(app.canvas)
})()
page.ts
import {
Child,
Component,
Container,
PageComponent,
Preloads,
Text,
} from 'morthics'
import styles from './page.style'
export class Main extends PageComponent {
preLoad(): Preloads {
{
return {
googleFonts: ['Snippet'],
}
}
}
render() {
return Container({}, new HelloWorld())
}
}
HelloWorld.ts
export class HelloWorld extends Component {
render() {
return Container(
{},
Text(styles.HelloWorld, 'Hello World!'),
)
}
}
page.style.ts
export default {
HelloWorld: {
style: {
fill: '#ffffff',
fontSize: 36,
fontFamily: 'Snippet',
},
},
}
Documentation
Application
To create an application on Morthics, the first thing to do is create an object of the Application
class
import { Application } from 'morthics'
const app = new Application(
{ resizeTo: window, backgroundColor: '#242424' },
Main
)
In the constructor of the Application
class, the first argument is passed to the object with the application settings, and the second argument is passed to PageComponent
Now we can add our application to the page, but before that we should make sure that the application has time to load and for this purpose the app object has a load field. Then all we need to do is to add the canvas to the desired part of the page.
import { Application } from 'morthics'
;(async () => {
const app = new Application(
{ resizeTo: window, backgroundColor: '#242424' },
Main
)
await app.load
document.body.appendChild(app.canvas)
})()
PageComponent
PageComponent
is a page component that is similar in its capabilities to a regular component.
Like a Component, it has a render method that returns the markup for the whole page
import { PageComponent, Container, Text } from 'morthics'
export class Main extends PageComponent {
render() {
return Container(
{},
Text({}, 'Hello World!'),
)
}
}
preLoad
The preLoad
method allows you to preload necessary data such as textures and fonts. To do this, return an object with the required fields from the preLoad
method
It is also worth noting that fonts from google fonts and other fonts are downloaded differently in the array googleFonts should be passed to the names of fonts, and in the array fonts links to download them
export class Main extends PageComponent {
preLoad(): Preloads {
{
return {
googleFonts: ['Montserrat', 'Snippet'],
textures: ['https://pixijs.com/assets/bunny.png'],
}
}
}
render() {
return Container({}, new HelloWorld())
}
}
Component
Сomponents in Morthics are created based on classes. Each component has its own life cycle and state. On different parts of the lifecycle you can hang event listeners, and the state can be changed, which will lead to the re-rendering of the component
This is an example of a simple counter, its logic can be simplified and not to create unnecessary components, but for a better understanding of working with components, props and state the button was placed in a separate component
page.ts
export class Main extends PageComponent {
preLoad(): Preloads {
{
return {
googleFonts: ['Montserrat', 'Snippet'],
textures: ['https://pixijs.com/assets/bunny.png'],
}
}
}
render() {
return Container({}, new HelloWorld())
}
}
Hi.ts
export class Hi extends Component {
beforeRender(): void {
this.state.count = 0
this.ref.Button = new CounterButton()
}
render() {
const { Button } = this.ref
return Container(
{ x: 10 },
Text(
{
style: {
fill: '#fff',
},
},
this.state.count
),
withProps(Button, {
count: this.state.count,
increment: () => this.state.count++,
})
)
}
}
CounterButton.ts
interface Props {
count: number
increment: () => void
}
export class CounterButton extends Component<Props> {
render(): Child {
const { count, increment } = this.props
return Container(
{},
Button(
{
onClick: increment,
y: 20,
},
count
)
)
}
}
State
State is the data that tells you which component is which at any given moment. Any component except PageComponent
has a state. It can be accessed via this.state and data can be written to it by normal assignment, but if you change the state, the component will be re-drawn
class CounterButton extends Component {
beforeRender(): void {
this.state.count = 0
}
render(): Child {
return Container(
{},
Text(
{
x: 130,
style: {
fill: '#fff',
fontFamily: 'Snippet',
},
},
this.state.count
),
Container(
{ y: 50 },
Button(
{
onClick: () => this.state.count++,
},
'increment'
),
Button(
{
x: 75,
onClick: () => this.state.count--,
},
'decrement'
)
)
)
}
}
It is also important to realize that if you change several states in a row or change the same state many times, it does not mean that each assignment will be re-rendered, but only when necessary
this.state.count++
this.state.count++
this.state.y *= 2
// 1 re-render
this.state.count++
setTimeout(() => {
this.state.count++
}, 1000)
// 2 re-renders
this.state.count = 0
setTimeout(() => {
this.state.count = 0
}, 1000)
// 0 re-renders
Props
Through props you can pass data to the component from its parent for this you need to use the withProps
function. If the props changes, the component will be redrawn. Props can be changed from a child, but then the re-rendering will not happen and unexpected behavior is possible, so it is better not to do this and if you really want to change its value, it is better to create a new variable
If necessary, a function can be passed to withProps
as a third parameter to say whether the props have changed and depending on the answer the component will either be redrawn or not
export class Main extends PageComponent {
render() {
return Container(
{},
withProps(new YellowButton(), { text: 'button 1' }),
withProps(
new YellowButton(),
{
text: 'button 2',
options: { x: 100, onClick: () => alert("It's button 2") },
},
(lastProps, newProps) => lastProps.text !== newProps.text
)
)
}
}
YellowButton.ts
interface Props {
text: string
options?: ButtonOptions
}
export class YellowButton extends Component<Props> {
render(): Child {
const { text, options = {} } = this.props
return Container(
{},
Button(
{
bgColor: 0xffff00,
textStyle: {
fill: '#fff',
},
...options,
},
text
)
)
}
}
ref
Ref
is the storage of this component. It performs the same functions as state, but with an important difference, which is that when ref is changed, the component is not re-rendered
Also, all the components you want to use should be written to ref
(ref is absent in PageComponent
, as it is not necessary to use it there)
class CounterButton extends Component {
beforeRender(): void {
this.ref.MyButton = new CounterButton()
this.ref.text = 'Text'
}
render(): Child {
const { MyButton, text } = this.ref
return Container(
{},
MyButton(
{},
text,
)
)
}
}
Sprite
Sprite
is the analog of img
in html it allows you to add images to a page
class Bunny extends Component {
render(): Child {
return Container(
{},
Sprite(
{ width: 50, height: 100 },
'https://pixijs.com/assets/bunny.png'
)
)
}
}
Container
Container
allows you to group elements
class Bunnies extends Component {
render(): Child {
return Container(
{ x: 50 },
Sprite(
{ width: 50, height: 100 }, // x == 50
'https://pixijs.com/assets/bunny.png'
),
Sprite(
{ x: 100, width: 50, height: 100 }, // x == 150
'https://pixijs.com/assets/bunny.png'
),
)
}
}
Text (beta)
Text
- allows you to write text on the page
export class HelloWorld extends Component {
render() {
return Container(
{},
Text(
{
style: {
fill: '#ffffff',
fontSize: 36,
fontFamily: 'Snippet',
},
},
'Hello World!'
)
)
}
}
Button (beta)
Button
creates a button on the page. Initially it has a standard look similar to the browser button
but it can be styled to your needs. You can place onClick
, onHover
, onUnHover
event listeners on the button. You can also change border
, button background, text styles, padding
and more
class YellowButton extends Component<Props> {
render(): Child {
const { text, options = {} } = this.props
return Container(
{},
Button(
{
bgColor: 0xffff00,
textStyle: {
fill: '#fff',
},
...options,
},
text
)
)
}
}
animation (beta)
animation
is a parameter that can be used with any standard component, it allows you to describe in a simple format the animation
to be set for that component. You need to specify keyframes for certain percentages of animation execution
Parameters related to animation
animationTime: number
// Animation execution timeanimationType: 'default' | 'linear'
// Withlinear
animationType
, animation occurs at the same speed at all stages and this is the fastest type of animation. In turn, withdefault
animation animation is not at a constant speed (by default, the speed changes according to a sinusoidal graph)animationFunction: [(x: number) => number, number,number]
// animationFunction and function value area. animationFunction is a function that increments the speed of animation execution at a particular point in time. It is important that the function must be continuous over the specified definition areastartAnimation: boolean
// by default it's true but if false then animation will not be applied
class MegaBunny extends Component {
beforeRender(): void {
this.state.y = 0
this.state.count = 0
this.state.startAnimation = false
}
jump() {
this.state.startAnimation = true
this.state.count++
}
render() {
return Container(
{
x: (globalThis.innerWidth - 200) / 2,
y: (globalThis.innerHeight - 400) / 2,
},
Sprite(
{
width: 100,
height: 100,
animation: {
50: {
y: -150,
},
100: {
y: 0,
},
},
animationTime: 500,
animationType: 'default',
animationFunction: [(x) => Math.cos(x) + 1, 0, Math.PI],
startAnimation: this.state.startAnimation,
},
'https://pixijs.com/assets/bunny.png'
),
Text(
{ style: { fill: 0xffffff }, y: 150, x: 40 },
this.state.count
),
Button(
{
onClick: () => this.jump(),
borderSize: [0],
y: 100,
x: 5,
paddingLeft: 5,
paddingRight: 5,
borderRadius: 10,
},
'jump'
)
)
}
}