Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Components/NewOptions/OptionsType/OptionsType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ function Options_Effects({isOpened,onGoBack,options,handleOptionsChange}:Options
<li>Performance indicator: <span className='Green_cl'>Good</span></li>
</ul>
</EffectChoose>
<EffectChoose onChange={onChange} name='Effect' title='Firework' textColor='effects' current={effect} value={'Firework'} >
<ul>
<li>Colorfull fireworks flying from keys when played</li>
<li><span className='Red_cl'>Warning: </span>Background Image does not work with this effect</li>
<li>Performance indicator: <span className='Purple_cl'>Mid</span></li>
</ul>
</EffectChoose>
</div>
</div>
)
Expand Down
16 changes: 7 additions & 9 deletions src/Components/Tracks/updatedTracks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,6 @@ const UpdatedTracks = ({width,height,Player,options,sound,number_of_white_keys}:
}
},[blocks])

/** Cancel animation frame when component is deleted */
useEffect(()=>{
return () => {
if(animation_frame.current !== 0){
window.cancelAnimationFrame(animation_frame.current);
}
}
},[])

/**
* main animation frame, renders blocks.
*/
Expand Down Expand Up @@ -100,6 +91,7 @@ const UpdatedTracks = ({width,height,Player,options,sound,number_of_white_keys}:
},[mainCtx.current, blocks, pianoBlack.current, EffectCtx.current])

//Here set up all functions/handlers which require blocks to exist
//Also setup clearing the animationFrame when page closes
useEffect(()=>{
if(blocks !== undefined){
Player.setEventHandler(add_event)
Expand All @@ -109,6 +101,12 @@ const UpdatedTracks = ({width,height,Player,options,sound,number_of_white_keys}:
}
main_animation_frame();
}

return () => {
if(animation_frame.current !== 0){
window.cancelAnimationFrame(animation_frame.current);
}
}
},[blocks])

return(
Expand Down
7 changes: 2 additions & 5 deletions src/Helpers/Blocks/pianoInteraction.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//TODO: Add option for Key Gradient to be like fire: https://codepen.io/Capse/pen/aNOeee
// Current RadialGradient is very basic and unatractive
// Also, that radial gradient could be prerendered and reused
//TODO: Combine white_ctx and black_ctx into one single canvas (goal - reduce .clearRect function as much as possible)
//TODO: Combine radial gradient with piano lighting up - generate offsets for canvas... (goal - reduce .clearRect function as much as possible)

import { keyInfo } from "../../Utils/TypesForMidi";
import { CanvasRoundRect, addShadow } from '../../Utils/CanvasFuntions';
Expand Down Expand Up @@ -69,7 +67,6 @@ export default class pianoInteraction{
*/
public render():void {
const HEIGHT_OFFSET = this.height - this.TR_CONF.piano_height_ratio * this.height;
//!! TO MANY CLEAR RECTS
this.main_ctx.clearRect(0,HEIGHT_OFFSET,this.width, this.white_key_height); //Clears only the bottom part of the screen, as upper was cleared earlier
this.black_ctx.clearRect(0,0,this.width, this.black_key_height ); //Here I don't know yet how to proceed, but It will stay like this
this.main_ctx.shadowBlur = 0;
Expand All @@ -78,10 +75,10 @@ export default class pianoInteraction{
const height = key.type === 'BLACK' ? this.black_key_height : this.white_key_height
if(key.type === "BLACK"){
CanvasRoundRect(this.black_ctx,key.color,key.position + 1,0 - 2,key.width,height,3);
addShadow(this.main_ctx,key.position + 1,HEIGHT_OFFSET,height - 5, key.width);
//addShadow(this.main_ctx,key.position + 1,HEIGHT_OFFSET,height - 5, key.width); //This shadow makes almost no change, but still takes computing time
}else{
CanvasRoundRect(this.main_ctx,key.color,key.position,HEIGHT_OFFSET,key.width,height,3);
addShadow(this.main_ctx,key.position,HEIGHT_OFFSET,height, key.width);
addShadow(this.main_ctx,key.position,HEIGHT_OFFSET,height, key.width); //Better to have gradient defined for keys (as it is always the same), and just fill rect
}
this.gradient.generateGradient(key.position + key.width/2, key.width * 1.5);
this.gradient.updateGradient();
Expand Down
2 changes: 1 addition & 1 deletion src/Helpers/Blocks/updatedBlocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class Blocks{
this.key_positions_map = this.__create_key_position_map(width,nr_of_keys,key_width);
this.positions_to_render_line = this.RenderOctaveLines()
this.key_interactor = new pianoInteraction(canvases.blackKeyCtx,canvases.mainCtx,this.width,height,TR_CONF,options);
this.effect_manager = new EffectsManager(canvases.effectsCtx, width, height - (height / 5), options.Effect, this.options);
this.effect_manager = new EffectsManager(canvases.effectsCtx, width, this.height, options.Effect, this.options);
//
//Binding
//
Expand Down
187 changes: 187 additions & 0 deletions src/Helpers/Effects/DesignedEffects/Stain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { Options } from "../../../Utils/TypesForOptions";
import Effect from "../_Effect";

interface Stain_Options{
max_width:number,
max_height:number,
offset_margin:number,
speed:number,
max_radius:number,
bounce:number,
}

export type { Stain_Options };

interface path_element{
pos_x:number,
pos_y:number,
radius:number,
opacity:number,
falling_speed:number,
opacity_fade_speed:number,
x_movement:number,
sin_opacity:number,
sin_opacity_nominator:number,
color:string //In HEX format
}

class Stain_Entity{
private gravitation:number = 0.08
private end_x:number
private end_y:number
private create_time:number = Date.now()
private speed_x:number = (Math.random() > 0.5 ? 1 : -1) * Math.random() * 2 + 0.5
private speed_y:number = Math.random() * 7.5 + 8.5 * -1
private stop_time:number = Math.random()*1500 + 1000
private initial_radius:number = Math.random() * this.configuration.max_radius + 0.5
private render_next:number = 0
private TT_MARGIN = 15
public static DEFAULT_CONF:Stain_Options = {
max_height:50,
max_width:90,
offset_margin:10,
max_radius:4,
speed:1,
bounce: 0.9
}
private path_element:path_element[];

constructor(start_x:number, start_y:number, private color:string[], private configuration:Stain_Options, private height:number ){
this.end_x = start_x;
this.end_y = start_y;
this.path_element = this._generate_shape();
}

public update():boolean{
if(Date.now() - this.create_time >= this.stop_time ){

let is_each_zero = false;
this.path_element.forEach((el,index) =>{
el.sin_opacity+=el.sin_opacity_nominator;
el.color = el.color.slice(0,7) + (Math.abs(Math.floor(el.opacity * Math.cos(el.sin_opacity))) < 16 ? '0' : '') + Math.abs(Math.floor(el.opacity * Math.cos(el.sin_opacity))).toString(16)
el.pos_y += el.falling_speed;
el.pos_x += el.x_movement;
el.x_movement -= this.gravitation * (el.x_movement < 0 ? -1 : 1);
el.falling_speed += this.gravitation;
if(index * this.TT_MARGIN <= this.render_next){
el.opacity -= el.opacity_fade_speed;
}
if(el.opacity > 0){
is_each_zero = true;
}
})
return is_each_zero;
}//If yet not the explosion time
this.end_x += this.speed_x;
this.end_y += this.speed_y;
this.speed_y += this.gravitation;
this.path_element.forEach(el =>{
el.pos_x = this.end_x;
el.pos_y = this.end_y;
})
return true;
}

public render(ctx:CanvasRenderingContext2D){
if(Date.now() - this.create_time >= this.stop_time){
this.path_element.forEach((el,index) =>{
if(index * this.TT_MARGIN <= this.render_next && el.opacity > 0){
ctx.beginPath();
ctx.fillStyle = el.color;
ctx.arc(el.pos_x,el.pos_y,el.radius,0,Math.PI * 2);
ctx.fill();
}
this.render_next+=1;
})
return;
}//else
const TRAIL_SIZE = 15;
for(let x = 0; x < TRAIL_SIZE; x++){
const pos_x = this.end_x - this.speed_x*(x+1)
const pos_y = this.end_y - this.speed_y*(x+1)
ctx.beginPath();
ctx.fillStyle = this.color[0] + Math.abs(Math.floor(255/ (0.5*(x+2)))).toString(16)
ctx.arc(pos_x, pos_y, this.initial_radius / (0.20*(x+5)), 0, Math.PI *2);
ctx.fill();
}
}

private _reach_destination():void{

}

private _generate_shape():Array<path_element>{
const DOTS_PER_ENTITY = 30;
const ENTITY_RADIUS = this.initial_radius;
const CENTER_X = this.end_x;
const CENTER_Y = this.end_y;
const path_arr:path_element[] = [];
for(let x = 0; x < DOTS_PER_ENTITY; x++){
const DENOM_X = Math.random() > 0.5 ? 1 : -1;
const DENOM_Y = Math.random() > 0.5 ? 1 : -1;
const element:path_element = {
opacity:Math.floor(204 * Math.random() + 50),
pos_x: CENTER_X,
pos_y: CENTER_Y,
radius: ENTITY_RADIUS * Math.random(),
falling_speed: Math.random() * 3.5 * ( Math.random() > 0.5 ? -1 : 0.2),
opacity_fade_speed: Math.random() * 3 + 1,
x_movement: Math.random() * 2.6 * (DENOM_X),
sin_opacity: DENOM_Y === -1 ? Math.random() * 90 : 0,
sin_opacity_nominator: DENOM_Y === -1 ? Math.random() * 0.1 + 0.2 : 0,
color: this.color[Math.floor(Math.random() * this.color.length)]
}
path_arr.push(element);
}
return path_arr;
}
}


export default class Stain extends Effect{
private entities:Stain_Entity[] = [];
private occupied_pos:{id:number,time:number}[] = [];

constructor(ctx: CanvasRenderingContext2D, width:number, height:number,private options: Options){
super(ctx, width, height);
}

public create_effect(pos_x: number, pos_y: number, key_width: number): void {
const TIMESPAN = 750;
const time_now = Date.now();
const el = this.occupied_pos.find(el => el.id === pos_x);
if(el){
if(time_now - el.time > TIMESPAN){
el.time = time_now;
this.entities.push(new Stain_Entity(pos_x + key_width/2,pos_y, [this.options.Color, this.options.ThinerBlockColor], Stain_Entity.DEFAULT_CONF, this.height))
return;
}
return;
}//if element does not exist
this.occupied_pos.push({
id:pos_x,
time:time_now
})
this.entities.push(new Stain_Entity(pos_x + key_width/2,pos_y, [this.options.Color, this.options.ThinerBlockColor], Stain_Entity.DEFAULT_CONF, this.height));


}

public update_effect(): void {
this.entities = this.entities.filter(entity => entity.update());
}

public render_effect(): void {
const vanish_speed = 1;
this.ctx.fillStyle = 'rgba(42,44,46,' + vanish_speed.toString() + ')';
this.ctx.fillRect(0,0,this.width,this.height);
this.entities.forEach(entity =>{
entity.render(this.ctx);
})
}

public handle_resze(width: number, height: number): void {
this.width = width;
this.height = height;
}
}
4 changes: 4 additions & 0 deletions src/Helpers/Effects/Effects.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* @deprecated File deprecated
*/

import { Options as OptionsType } from "../../Utils/TypesForOptions";
import {Fountain, DancingLines, HexagonEffect, StickyBalls, Firework,Sparks, EmptyEffect,DNA} from '../CanvasEffects';

Expand Down
6 changes: 5 additions & 1 deletion src/Helpers/Effects/EffectsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Options as OptionsType } from '../../Utils/TypesForOptions';
*/
import Sparks from './DesignedEffects/Sparks';
import Squared from './DesignedEffects/Squared';
import Stain from './DesignedEffects/Stain';
import Blank from './DesignedEffects/Blank';

/**
Expand Down Expand Up @@ -34,8 +35,11 @@ export default class EffectsManager{
case 'Squared':
this.effects = new Squared(ctx,width,height,2.5,3,[options.Color,options.ThinerBlockColor],0.05);
break;
case "Firework":
this.effects = new Stain(ctx,width,height, options);
break;
case "None":
this.effects = new Blank(ctx, 0, 0)
this.effects = new Blank(ctx, 0, 0);
break
default:
this.effects = new Blank(ctx, 0, 0);
Expand Down
2 changes: 1 addition & 1 deletion src/Utils/TypesForOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface Options{
ShadowColor:string,
EffectsColor:string,
OctaveLines:boolean,
Effect:"Squares" | "Sparks" | "None",
Effect:"Squares" | "Sparks" | "Firework" | "None",
ThinerBlockColor:string,
}

Expand Down