I work with sounds in a browser game. I wrote sound manager. everything works fine, but not in Google chrome. I handled the error "uncaught (in promise) domexception", after the sounds were played in 50 percent of cases, in other cases it returns the error DOMException. What could be the problem?
export class AudioFile{
private audio: HTMLAudioElement;
private fileMP3: string;
private fileOGG: string;
private volume = 1;
private loop = false;
constructor(MP3:string, OGG:string) {
this.audio = new Audio();
this.fileMP3 = MP3;
this.fileOGG = OGG;
this.audio.canPlayType('audio/mpeg') ? this.audio.src = this.fileMP3 : this.audio.src = this.fileOGG;
this.audio.load();
this.audio.volume = this.volume;
this.audio.loop = this.loop;
}
public play() {
this.audio.currentTime = 0;
const playPromise = this.audio.play();
if (playPromise !== undefined) {
playPromise.then(_ => {
})
.catch(error => {
console.log(error);
});
}
}
public stop() {
this.audio.pause();
}
}
``````````````sound manager`````````````
export class SoundManager {
private sounds = new Map();
private static _soundManager: SoundManager;
constructor(){
if (SoundManager._soundManager) {
throw new Error("Instantiation failed: "+
"use Singleton.getInstance() instead of new.");
}
SoundManager._soundManager = this;
}
public static get instance(): SoundManager {
if (this._soundManager)
return this._soundManager;
else
return this._soundManager = new SoundManager();
}
preload() {
const pathMP3 = SoundConfig.PATHMP3;
const pathOGG = SoundConfig.PATHOGG;
for (const item in SoundConfig.SOUNDS) {
const name = SoundConfig.SOUNDS[item].NAME;
this.sounds.set(name, new AudioFile(pathMP3 + name + '.mp3', pathOGG + name + '.ogg'));
}
}
getSound(id: string): AudioFile {
return this.sounds.get(id);
}
}
Thank you spendr.
error: DOMException
code: 0
message: "play() failed because the user didn't interact with the document first.
Game runs through the iframe and I was needed to add a feature policy for autoplay.
<iframe src="..." allow="autoplay">
The article that helped me in solving the problem
Related
I have a bunch of entities I've declared:
export class Meeting implements IHasId {
id = 0;
locationId = 0;
meetTime = new Date();
isFinalized = false;
imageId: number = null;
description = '';
name = '';
}
I have a generic crud service which handles resolving these to its original type
export class ApiResourceBaseService<T extends IHasId> {
get(id: number): Observable<T> {
return this.http.get<T>(`${this.apiUrl}/${this.route}/${id}`);
}
}
Typescript Generics are only superficial, so when I call get<T> typescript assumes my json is correct. However, My date objects are not resolved properly, they are resolved as string.
I have several entities, It would be annoying to create custom constructors/adapters to parse the dates.
does anyone have a better idea for automatically resolve dates?
I've created an interceptor to transform the data, In the future I'll probably just have auto generate my models and api resources from the api documentation and avoid this but for now I'm just using this hack:
export class ModelBinderInterceptor implements HttpInterceptor {
// regex of ISO 8601 Date.
DATE_TIME_TIMEZONE_REGEXP =
/^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
constructor() {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
event = event.clone({body: this.modifyBody(event.body)});
}
return event;
}));
}
private modifyBody(body: any) {
return this.deserializeDates(body);
}
private deserializeDates(obj) {
if ((!(obj instanceof Object)) || (isString(obj))) {
return obj;
}
for (const key of Object.keys(obj)) {
const value = obj[key];
let date;
if (isString(value) && (this.DATE_TIME_TIMEZONE_REGEXP.test(value))) {
date = new Date(value);
// the parsing above may fail, in which case we use isNaN to check if the date is valid
if (isNaN(date.getTime())) {
return;
}
obj[key] = date;
}
this.deserializeDates(value);
}
return obj;
}
}
Hy every one,
I'm starting to learn Angular and I've decided to start with the implementation of the Langton Ant algorithm.
Project
The source of the standalone viewer is here: https://github.com/Phreno/langton
The source of the Angular version is here: https://stackblitz.com/edit/langton-ng?embed=1&file=app/langton/langton.main.component.ts&view=editor
I use a HTML5 canvas to display the walk.
Template
<p>Langton ant</p>
<canvas #canvas_box width='420' height='420' style="background: #fff; margin:20px"></canvas>
Component
import { LangtonGridService } from './langton.grid.service';
import { CellCoordinate } from './grid/grid.coordinate'
import { Component, ViewChild, AfterViewInit } from '#angular/core'
import { LangtonStep } from './langton.utils'
#Component({
selector: 'langton-main',
templateUrl: "template/langton.template.html",
})
export class LangtonMainComponent implements AfterViewInit{
DIRECTION_UP:number=2;
DIRECTION_RIGHT:number=1;
DIRECTION_LEFT:number=-1;
DIRECTION_DOWN:number=-2;
COLOR_FOG:string='white';
COLOR_GRID:string='gainsboro';
COLOR_FLAG:string='red';
COLOR_FREE:string='aqua';
COLOR_CURRENT:string='black';
MAX_ITERATION:number=15000;
STEP_TIMER:number=100;
grid:LangtonGridService;
#ViewChild('canvas_box')
canvas:HTMLCanvasElement;
lineCount:number;
columnCount:number;
state:any={};
roadmap:Array<LangtonStep>=[];
ngAfterViewInit(){
this.setGrid(new LangtonGridService(this.getCanvas()));
this.getGrid().drawBoard(this.COLOR_GRID);
this.walk();
}
private getCanvas():HTMLCanvasElement{
return this.canvas;
}
private getGrid():LangtonGridService{
return this.grid;
}
private setGrid(grid:LangtonGridService):void{
this.grid = grid;
}
private getState():any{
return this.state;
}
private getRoadmap():Array<LangtonStep>{
return this.roadmap;
}
private setLineCount():void{
this.lineCount = this.getGrid().computeLines().length;
}
private setColumnCount():void{
this.columnCount = this.getGrid().computeColumns().length;
}
private getLineCount():number{
return this.lineCount;
}
private getColumnCount():number{
return this.columnCount;
}
private getStart():LangtonStep{
var middleColumn:number = Math.round(this.getColumnCount()/2);
var middleLine:number = Math.round(this.getColumnCount()/2);
var start:CellCoordinate=new CellCoordinate(middleColumn, middleLine);
var color=this.COLOR_FLAG;
var direction=this.DIRECTION_UP;
var step:LangtonStep = new LangtonStep(start, color, direction);
return step;
};
private memorize(step:LangtonStep):void{
this.getState()[step.getAddress()]=step;
}
private track(step:LangtonStep):void{
this.getRoadmap().push(step);
}
private tracked(step:LangtonStep):boolean{
var state:string=typeof(this.getState()[step.getAddress()]);
var exist:boolean='undefined'.localeCompare(state)!==0;
return exist;
}
private turnRight(step:LangtonStep):LangtonStep{
var cellCoordinate:CellCoordinate;
var direction:number;
var color:string;
var next:LangtonStep;
switch(step.direction){
case this.DIRECTION_UP:
cellCoordinate=new CellCoordinate(step.coordinate.column+1, step.coordinate.line);
direction = this.DIRECTION_RIGHT;
break;
case this.DIRECTION_DOWN:
cellCoordinate=new CellCoordinate(step.coordinate.column-1, step.coordinate.line);
direction = this.DIRECTION_LEFT;
break;
case this.DIRECTION_LEFT:
cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line+1);
direction = this.DIRECTION_UP;
break;
case this.DIRECTION_RIGHT:
cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line-1);
direction = this.DIRECTION_RIGHT;
break;
default:
console.error('erreur dans la direction');
};
color=this.COLOR_FLAG;
next = new LangtonStep(cellCoordinate,color,direction);
return next;
}
private turnLeft(step:LangtonStep):LangtonStep{
var cellCoordinate:CellCoordinate;
var direction:number;
var color:string;
var next:LangtonStep;
switch(step.direction){
case this.DIRECTION_UP:
cellCoordinate=new CellCoordinate(step.coordinate.column-1, step.coordinate.line);
direction = this.DIRECTION_RIGHT;
break;
case this.DIRECTION_DOWN:
cellCoordinate=new CellCoordinate(step.coordinate.column+1, step.coordinate.line);
direction = this.DIRECTION_LEFT;
break;
case this.DIRECTION_LEFT:
cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line-1);
direction = this.DIRECTION_UP;
break;
case this.DIRECTION_RIGHT:
cellCoordinate=new CellCoordinate(step.coordinate.column, step.coordinate.line+1);
direction = this.DIRECTION_RIGHT;
break;
default:
console.error('erreur dans la direction');
};
color=this.COLOR_FREE;
next = new LangtonStep(cellCoordinate,color,direction);
return next;
};
private getColorFromMemory(step:LangtonStep):string{
var color:string;
if(this.tracked(step)){
color=this.getState()[step.getAddress()].color;
} else {
color = this.COLOR_FOG;
}
return color;
};
private print(step:LangtonStep):void{
this.getGrid().fillCell(step.coordinate, step.color);
};
private enlight(cell:CellCoordinate):void{
this.getGrid().strokeCell(cell, this.COLOR_CURRENT);
}
private darken(cell:CellCoordinate):void{
this.getGrid().strokeCell(cell, this.COLOR_GRID);
}
private turn(step:LangtonStep):LangtonStep{
var next:LangtonStep;
switch(step.color){
case this.COLOR_FOG:
next=this.turnRight(step);
break;
case this.COLOR_FLAG:
next=this.turnLeft(step);
break;
case this.COLOR_FREE:
next=this.turnRight(step);
break;
default:
console.error('La couleur venue du ciel');
break;
}
next.setColor(this.getColorFromMemory(next));
return next;
}
private walk(step:LangtonStep=this.getStart()):void{
var self:LangtonMainComponent=this;
var forward:Function=function(step:LangtonStep):LangtonStep{
var next:LangtonStep=self.turn(step);
self.print(step);
self.darken(step.coordinate);
self.enlight(next.coordinate);
self.memorize(step);
self.track(step);
return next;
}
var iteration=0;
var run:number = setInterval(function():void{
if(self.MAX_ITERATION===iteration++){
clearInterval(run);
} else{
step=forward(step);
}
}, self.STEP_TIMER);
}
}
Problem
ERROR TypeError: Cannot read property 'getContext' of undefined
at GridDrawer.getContext (grid.drawer.ts:17)
at GridDrawer.openLayer (grid.drawer.ts:21)
at LangtonGridService.drawBoard (langton.grid.service.ts:109)
at LangtonMainComponent.ngAfterViewInit (langton.main.component.ts:51)
at callProviderLifecycles (provider.js:585)
at callElementProvidersLifecycles (provider.js:556)
at callLifecycleHooksChildrenFirst (provider.js:540)
at checkAndUpdateView (view.js:390)
at callViewAction (view.js:732)
at execComponentViewsAction (view.js:661)
Question
My first try was to set the canvas in the component constructor.
After some search, I understand that I've to import AfterViewInit interface to work with.
But in each case I reach the same error about undefined canvas.
I'm pretty sure the solution is obvious but I can't found it.
Thanks a lot for your help and have a good day.
Etienne.
I finally found the solution. The problem comes from HTMLCanvasElement and I resolve the binding using ElementRef instead.
Template
<canvas #canvas_box width='420' height='420' style="background: #fff; margin:20px"></canvas>
Component
export class LangtonMainComponent implements AfterViewInit{
/* ... */
#ViewChild('canvas_box') canvas:ElementRef;
ngAfterViewInit(){
this.canvas.nativeElement.width = this.grid.getCanvasWidth();
this.canvas.nativeElement.height = this.grid.getCanvasHeight();
this.drawBoard();
this.walk();
}
/* ... */
}
Any kind soul can guide me how to use signalR on an existing mvc project to poll data in real time i'd be greatly appreciate.
example code:
[controller]
private ApplicationDbContext db = new ApplicationDbContext();
public PartialViewResult Chat(string people) // <---need to send real time data to partial
{
var model = new MessageVM()
{
sender = User.Identity.Name;,
messageList = db.messages.Where(x => x.receiver == people).ToList().Take(30)
};
return PartialView("_chat", model);
}
[view]
#Ajax.ActionLink(item.name, "Chat", new { people = item.name }, new AjaxOptions()
{ HttpMethod = "GET", UpdateTargetId = "divChat", InsertionMode = InsertionMode.Replace })
<div id="divChat"></div> // <---this area need real-time messages data from controller.
First create your signalr connection in js in client side. something like:
function signalrconnection() {
$.connection.hub.url = "http://localhost:54321/signalr";
chat = $.connection.myHub;
if (chat != undefined) {
$.connection.hub.start()
.done(function () {
chat.server.send("client", "Status\tasking for status");
chat = $.connection.myHub;
})
.fail(function () { NoLTConnectionAlert(); });
}
else {
///do something.
}
}
return chat;
}
Then add signalr call to your $(document).ready(function ()) in your js something like:
$(document).ready(function () {
chat = signalrconnection();
intervalstatus = setInterval(checkstatus, 1000);
// Create a function that the hub can call to broadcast messages.
chat.client.addMessage = function (name, message) {}
}
In your controller you should have a class for hub and method inside like:
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.Caller.addMessage("parameter", reply);
}
}
Then again you should handle Clients.Caller.addMessage in you js to update <div id="divChat"></div>
I am trying to create knockout.js component that is getting data from HTML5 Websocket. Websocket code is in separate script e.g. util.js. I am able to connect and get data from socket, but dont know how correctly to set corresponding property in component`s ViewModel.
Websocket - util.js:
var options = {
server: '127.0.0.1',
port: '12345'
};
var socket, loadedFlag;
var timeout = 2000;
var clearTimer = -1;
var data = {};
function handleErrors(sError, sURL, iLine)
{
return true;
};
function getSocketState()
{
return (socket != null) ? socket.readyState : 0;
}
function onMessage(e)
{
data=$.parseJSON(e.data);
// ???? Is it possible to have here something like
// ???? viewModel.getDataWS1(data);
}
function onError()
{
clearInterval(clearTimer);
socket.onclose = function () {
loadedFlag = false;
};
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onClose()
{
loadedFlag = false;
clearInterval(clearTimer);
clearTimer = setInterval("connectWebSocket()", timeout);
}
function onOpen()
{
clearInterval(clearTimer);
console.log("open" + getSocketState());
}
function connectWebSocket()
{
if ("WebSocket" in window)
{
if (getSocketState() === 1)
{
socket.onopen = onOpen;
clearInterval(clearTimer);
console.log(getSocketState());
}
else
{
try
{
host = "ws://" + options.server + ":" + options.port;
socket = new WebSocket(host);
socket.onopen = onOpen;
socket.onmessage = function (e) {
onMessage(e);
};
socket.onerror = onError;
socket.onclose = onClose;
}
catch (exeption)
{
console.log(exeption);
}
}
}
}
Component (productDisplay.js) - creating so that is can be used on multiple pages:
define([
'jquery',
'app/models/productDisplayModel',
'knockout',
'mapping',
'socket'
],
function ($, model, ko, mapping) {
ko.components.register('product', {
viewModel: {require: 'app/models/productModel'},
template: {require: 'text!app/views/product.html'}
});
});
Product ViewModel (productModel.js) - where I struggle to set viewModel property to data from websocket:
var viewModel = {};
define(['knockout', 'mapping', 'jquery'], function (ko, mapping, $) {
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.getDataWS1 = function () {
//Websocket has not connected and returned data yet, so data object is empty
// ???? Is there anyway I can add something like promise so that the value is set once socket is connected?
this.products(data);
};
// apply binding on page load
$(document).ready(function () {
connectToServer1();
viewModel = new MyViewModel();
ko.applyBindings(viewModel);
viewModel.getDataWS1();
});
});
Thank you for any ideas.
You can update an observable when you get a message in the following manner:
util.js
function onMessage(e) {
var productData = $.parseJSON(e.data);
viewModel.addNewProduct(productData);
}
productModel.js
function Product(name, rating) {
this.name = name;
this.userRating = ko.observable(rating || null);
}
function MyViewModel() {
this.products = ko.observableArray(); // Start empty
}
MyViewModel.prototype.addNewProduct(product) {
var newProduct = new Product(product.name, product.rating);
this.products.push(newProduct);
}
Basically the idea is that when you get a message (in onMessage function), you will parse the data and call a function in your viewmodel to add the message data to the viewmodel properties (observables, observableArrays, etc.)
I want to have a variable in a TypeScript class that is of the type "boolean isVisible()".
How do I declare it?
How do I assign this function for another instantiated object to this variable?
How do I call this function?
ps - This seems so basic but 10 minutes of searching and I couldn't find it.
function boolfn() { return true; }
function strfn() { return 'hello world'; }
var x: () => boolean;
x = strfn; // Not OK
x = boolfn; // OK
var y = x(); // y: boolean
Here's one way of doing it, though I'll be happy to work with you to figure out exactly what you're trying to achieve.
export module Sayings {
export class Greeter {
isVisible(): boolean {
return true;
}
}
}
var greeter = new Sayings.Greeter();
var visible = greeter.isVisible();
You could also use a property instead of a function. Your original question talks about a "variable" and a "function" as if they're the same thing, but that's not necessarily the case.
export module Sayings {
export class Greeter {
isVisible: boolean = false;
}
}
var greeter = new Sayings.Greeter();
var visible = greeter.isVisible;
greeter.isVisible = true;
Or something like this maybe?
export module Sayings {
export class Greeter {
constructor(public isVisible: () => boolean) {
}
}
}
var someFunc = () => {
return false;
}
var greeter = new Sayings.Greeter(someFunc);
var visible = greeter.isVisible();