refactored effects dir and changed effects exported name
This commit is contained in:
46
src/effects/ICanvasEffect.ts
Normal file
46
src/effects/ICanvasEffect.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2020 Nurjin Jafar
|
||||
Copyright 2020 Nordeck IT + Consulting GmbH.
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Defines the constructor of a canvas based room effect
|
||||
*/
|
||||
export interface ICanvasEffectConstructable {
|
||||
/**
|
||||
* @param {{[key:string]:any}} options? Optional animation options
|
||||
* @returns ICanvasEffect Returns a new instance of the canvas effect
|
||||
*/
|
||||
new(options?: { [key: string]: any }): ICanvasEffect
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the interface of a canvas based room effect
|
||||
*/
|
||||
export default interface ICanvasEffect {
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvas The canvas instance as the render target of the animation
|
||||
* @param {number} timeout? A timeout that defines the runtime of the animation (defaults to false)
|
||||
*/
|
||||
start: (canvas: HTMLCanvasElement, timeout?: number) => Promise<void>,
|
||||
/**
|
||||
* Stops the current animation
|
||||
*/
|
||||
stop: () => Promise<void>,
|
||||
/**
|
||||
* Returns a value that defines if the animation is currently running
|
||||
*/
|
||||
isRunning: boolean
|
||||
}
|
||||
193
src/effects/confetti/index.ts
Normal file
193
src/effects/confetti/index.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
Copyright 2020 Nurjin Jafar
|
||||
Copyright 2020 Nordeck IT + Consulting GmbH.
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import ICanvasEffect from '../ICanvasEffect';
|
||||
|
||||
|
||||
export type ConfettiOptions = {
|
||||
/**
|
||||
* max confetti count
|
||||
*/
|
||||
maxCount: number,
|
||||
/**
|
||||
* particle animation speed
|
||||
*/
|
||||
speed: number,
|
||||
/**
|
||||
* the confetti animation frame interval in milliseconds
|
||||
*/
|
||||
frameInterval: number,
|
||||
/**
|
||||
* the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
|
||||
*/
|
||||
alpha: number,
|
||||
/**
|
||||
* use gradient instead of solid particle color
|
||||
*/
|
||||
gradient: boolean,
|
||||
}
|
||||
|
||||
type ConfettiParticle = {
|
||||
color: string,
|
||||
color2: string,
|
||||
x: number,
|
||||
y: number,
|
||||
diameter: number,
|
||||
tilt: number,
|
||||
tiltAngleIncrement: number,
|
||||
tiltAngle: number,
|
||||
}
|
||||
|
||||
export const DefaultOptions: ConfettiOptions = {
|
||||
maxCount: 150,
|
||||
speed: 3,
|
||||
frameInterval: 15,
|
||||
alpha: 1.0,
|
||||
gradient: false,
|
||||
};
|
||||
|
||||
export default class Confetti implements ICanvasEffect {
|
||||
private readonly options: ConfettiOptions;
|
||||
|
||||
constructor(options: { [key: string]: any }) {
|
||||
this.options = {...DefaultOptions, ...options};
|
||||
}
|
||||
|
||||
private context: CanvasRenderingContext2D | null = null;
|
||||
private supportsAnimationFrame = window.requestAnimationFrame;
|
||||
private colors = ['rgba(30,144,255,', 'rgba(107,142,35,', 'rgba(255,215,0,',
|
||||
'rgba(255,192,203,', 'rgba(106,90,205,', 'rgba(173,216,230,',
|
||||
'rgba(238,130,238,', 'rgba(152,251,152,', 'rgba(70,130,180,',
|
||||
'rgba(244,164,96,', 'rgba(210,105,30,', 'rgba(220,20,60,'];
|
||||
|
||||
private lastFrameTime = Date.now();
|
||||
private particles: Array<ConfettiParticle> = [];
|
||||
private waveAngle = 0;
|
||||
|
||||
public isRunning: boolean;
|
||||
|
||||
public start = async (canvas: HTMLCanvasElement, timeout = 3000) => {
|
||||
if (!canvas) {
|
||||
return;
|
||||
}
|
||||
this.context = canvas.getContext('2d');
|
||||
this.particles = [];
|
||||
const count = this.options.maxCount;
|
||||
while (this.particles.length < count) {
|
||||
this.particles.push(this.resetParticle({} as ConfettiParticle, canvas.width, canvas.height));
|
||||
}
|
||||
this.isRunning = true;
|
||||
this.runAnimation();
|
||||
if (timeout) {
|
||||
window.setTimeout(this.stop, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
public stop = async () => {
|
||||
this.isRunning = false;
|
||||
}
|
||||
|
||||
private resetParticle = (particle: ConfettiParticle, width: number, height: number): ConfettiParticle => {
|
||||
particle.color = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')');
|
||||
if (this.options.gradient) {
|
||||
particle.color2 = this.colors[(Math.random() * this.colors.length) | 0] + (this.options.alpha + ')');
|
||||
} else {
|
||||
particle.color2 = particle.color;
|
||||
}
|
||||
particle.x = Math.random() * width;
|
||||
particle.y = Math.random() * -height;
|
||||
particle.diameter = Math.random() * 10 + 5;
|
||||
particle.tilt = Math.random() * -10;
|
||||
particle.tiltAngleIncrement = Math.random() * 0.07 + 0.05;
|
||||
particle.tiltAngle = Math.random() * Math.PI;
|
||||
return particle;
|
||||
}
|
||||
|
||||
private runAnimation = (): void => {
|
||||
if (!this.context || !this.context.canvas) {
|
||||
return;
|
||||
}
|
||||
if (this.particles.length === 0) {
|
||||
this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
|
||||
} else {
|
||||
const now = Date.now();
|
||||
const delta = now - this.lastFrameTime;
|
||||
if (!this.supportsAnimationFrame || delta > this.options.frameInterval) {
|
||||
this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
|
||||
this.updateParticles();
|
||||
this.drawParticles(this.context);
|
||||
this.lastFrameTime = now - (delta % this.options.frameInterval);
|
||||
}
|
||||
requestAnimationFrame(this.runAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private drawParticles = (context: CanvasRenderingContext2D): void => {
|
||||
if (!this.context || !this.context.canvas) {
|
||||
return;
|
||||
}
|
||||
let x; let x2; let y2;
|
||||
for (const particle of this.particles) {
|
||||
this.context.beginPath();
|
||||
context.lineWidth = particle.diameter;
|
||||
x2 = particle.x + particle.tilt;
|
||||
x = x2 + particle.diameter / 2;
|
||||
y2 = particle.y + particle.tilt + particle.diameter / 2;
|
||||
if (this.options.gradient) {
|
||||
const gradient = context.createLinearGradient(x, particle.y, x2, y2);
|
||||
gradient.addColorStop(0, particle.color);
|
||||
gradient.addColorStop(1.0, particle.color2);
|
||||
context.strokeStyle = gradient;
|
||||
} else {
|
||||
context.strokeStyle = particle.color;
|
||||
}
|
||||
context.moveTo(x, particle.y);
|
||||
context.lineTo(x2, y2);
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
private updateParticles = () => {
|
||||
if (!this.context || !this.context.canvas) {
|
||||
return;
|
||||
}
|
||||
const width = this.context.canvas.width;
|
||||
const height = this.context.canvas.height;
|
||||
let particle: ConfettiParticle;
|
||||
this.waveAngle += 0.01;
|
||||
for (let i = 0; i < this.particles.length; i++) {
|
||||
particle = this.particles[i];
|
||||
if (!this.isRunning && particle.y < -15) {
|
||||
particle.y = height + 100;
|
||||
} else {
|
||||
particle.tiltAngle += particle.tiltAngleIncrement;
|
||||
particle.x += Math.sin(this.waveAngle) - 0.5;
|
||||
particle.y += (Math.cos(this.waveAngle) + particle.diameter + this.options.speed) * 0.5;
|
||||
particle.tilt = Math.sin(particle.tiltAngle) * 15;
|
||||
}
|
||||
if (particle.x > width + 20 || particle.x < -20 || particle.y > height) {
|
||||
if (this.isRunning && this.particles.length <= this.options.maxCount) {
|
||||
this.resetParticle(particle, width, height);
|
||||
} else {
|
||||
this.particles.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/effects/effectUtilities.ts
Normal file
25
src/effects/effectUtilities.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2020 Nurjin Jafar
|
||||
Copyright 2020 Nordeck IT + Consulting GmbH.
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Checks a message if it contains one of the provided emojis
|
||||
* @param {Object} content The message
|
||||
* @param {Array<string>} emojis The list of emojis to check for
|
||||
*/
|
||||
export const containsEmoji = (content: { msgtype: string, body: string }, emojis: Array<string>): boolean => {
|
||||
return emojis.some((emoji) => content.body && content.body.includes(emoji));
|
||||
}
|
||||
90
src/effects/index.ts
Normal file
90
src/effects/index.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2020 Nurjin Jafar
|
||||
Copyright 2020 Nordeck IT + Consulting GmbH.
|
||||
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import { _t, _td } from "../languageHandler";
|
||||
|
||||
export type Effect<TOptions extends { [key: string]: any }> = {
|
||||
/**
|
||||
* one or more emojis that will trigger this effect
|
||||
*/
|
||||
emojis: Array<string>;
|
||||
/**
|
||||
* the matrix message type that will trigger this effect
|
||||
*/
|
||||
msgType: string;
|
||||
/**
|
||||
* the room command to trigger this effect
|
||||
*/
|
||||
command: string;
|
||||
/**
|
||||
* a function that returns the translated description of the effect
|
||||
*/
|
||||
description: () => string;
|
||||
/**
|
||||
* a function that returns the translated fallback message. this message will be shown if the user did not provide a custom message
|
||||
*/
|
||||
fallbackMessage: () => string;
|
||||
/**
|
||||
* animation options
|
||||
*/
|
||||
options: TOptions;
|
||||
}
|
||||
|
||||
type ConfettiOptions = {
|
||||
/**
|
||||
* max confetti count
|
||||
*/
|
||||
maxCount: number,
|
||||
/**
|
||||
* particle animation speed
|
||||
*/
|
||||
speed: number,
|
||||
/**
|
||||
* the confetti animation frame interval in milliseconds
|
||||
*/
|
||||
frameInterval: number,
|
||||
/**
|
||||
* the alpha opacity of the confetti (between 0 and 1, where 1 is opaque and 0 is invisible)
|
||||
*/
|
||||
alpha: number,
|
||||
/**
|
||||
* use gradient instead of solid particle color
|
||||
*/
|
||||
gradient: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* This configuration defines room effects that can be triggered by custom message types and emojis
|
||||
*/
|
||||
export const CHAT_EFFECTS: Array<Effect<{ [key: string]: any }>> = [
|
||||
{
|
||||
emojis: ['🎊', '🎉'],
|
||||
msgType: 'nic.custom.confetti',
|
||||
command: 'confetti',
|
||||
description: () => _td("Sends the given message with confetti"),
|
||||
fallbackMessage: () => _t("sends confetti") + " 🎉",
|
||||
options: {
|
||||
maxCount: 150,
|
||||
speed: 3,
|
||||
frameInterval: 15,
|
||||
alpha: 1.0,
|
||||
gradient: false,
|
||||
},
|
||||
} as Effect<ConfettiOptions>,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user