Rewrite module scripts to classic scripts in JavaScript - javascript

I developed a game using module scripts in JavaScript. Basically import and export statement inside my code. The game is only available running local server but I want it available through index.html. If I open it using index.html it throws
Access to Script at path from origin 'null' has been blocked by CORS policy: Invalid response. Origin 'null' is therefore not allowed access.
<!DOCTYPE html>
<html>
<head>
<title>My Game</title>
<style>
body{
margin: 0;
height: 100%;
width: 100%;
}
#screen {
display: block;
height: 100vh;
width: 100vw;
margin: 0;
image-rendering: pixelated;
}
</style>
<script type="module" src="js/main.js"></script>
</head>
<body>
<canvas id="screen" width="250" height="330"></canvas>
</body>
</html>
I think I need to rewrite my JS scripts to classic scripts.
Main.js
import Camera from './Camera.js';
import Entity from './Entity.js';
import PlayerController from './traits/PlayerController.js';
import Timer from './Timer.js';
import { loadFont } from './loaders/font.js';
import { loadEntities } from './entities.js';
import { createLevelLoader, createRandomEntity } from './loaders/level.js';
import { setupKeyboard } from './input.js';
import { createCollisionLayer } from './layers/collision.js';
import { createDashboardLayer } from './layers/dashboard.js';
import { createGameOverLayer } from './layers/gameover.js';
function createPlayerEnv(playerEntity) {
const playerEnv = new Entity();
const playerControl = new PlayerController();
playerControl.checkpoint.set(64, 64);
playerControl.setPlayer(playerEntity);
playerEnv.addTrait(playerControl);
return playerEnv;
}
async function main(canvas) {
const context = canvas.getContext('2d');
const [entityFactory, font] = await Promise.all([loadEntities(), loadFont()]);
const loadLevel = await createLevelLoader(entityFactory);
const level = await loadLevel('level');
const camera = new Camera();
const user = entityFactory.user();
const playerEnv = new createPlayerEnv(user);
level.entities.add(playerEnv);
//Bounding Box:
level.comp.layers.push(createCollisionLayer(level));
level.comp.layers.push(createDashboardLayer(font, playerEnv));
timer.start();
}
const canvas = document.getElementById('screen');
main(canvas);
Script with exports
import Keyboard from './KeyboardState.js';
export function setupKeyboard(pacman) {
const input = new Keyboard();
input.addMapping('KeyP', keyState => {
if (keyState) {
pacman.jump.start();
} else {
pacman.jump.cancel();
}
});
input.addMapping('KeyO', keyState => {
pacman.turbo(keyState);
});
input.addMapping('KeyD', keyState => {
pacman.go.dir += keyState ? 1 : -1;
});
input.addMapping('KeyA', keyState => {
pacman.go.dir += keyState ? -1 : 1;
});
return input;
}
Could you provide an example on how to rewrite module scripts to classic scripts? Thanks!

Related

ReactJS add Iframe inside Canvas context

I have a ReactJS code like this
let context = canvas.getContext('2d');
let renderSize = (video1ScaleMode === 'fill') ? fillSize(video1Size, canvasSize, 1) : fitSize(video1Size, canvasSize, 1);
let xOffset = (canvasSize.width - renderSize.width) / 2;
let yOffset = (canvasSize.height - renderSize.height) / 2;
context.drawImage(video1, xOffset, yOffset, renderSize.width, renderSize.height);
This loads the camera inside the canvas. This is working fine.
I want to add a webpage (with animations) behind the camera view. For that I tried to add IFrame (because the URL is from the server)
For that I tried the following code
iframe.js
import React, { Component } from 'react'
import { createPortal } from 'react-dom'
export class IFrame extends Component {
constructor(props) {
super(props)
this.state = {
mountNode: null
}
this.setContentRef = (contentRef) => {
this.setState({
mountNode: contentRef?.contentWindow?.document?.body
})
}
}
render() {
const { children, ...props } = this.props
const { mountNode } = this.state
return (
<iframe
{...props}
ref={this.setContentRef}
>
{mountNode && createPortal(children, mountNode)}
</iframe>
)
}
}
My usage is like this
import { IFrame } from './iframe'
const MyComp = () => (
<IFrame>
https://www.helloworld.com
</IFrame>
)
context.drawImage(MyComp, xOffset, yOffset, renderSize.width, renderSize.height);
For that I got error like this
**
Uncaught TypeError: Failed to execute 'drawImage' on
'CanvasRenderingContext2D': The provided value is not of type
'(CSSImageValue or HTMLCanvasElement or HTMLImageElement or
HTMLVideoElement or ImageBitmap or OffscreenCanvas or SVGImageElement
or VideoFrame)'.
at renderFrame
**
I understand that IFrame component is not like HTMLCanvasElement or ImageElement or VideoElement.
I would like to know how I can add the IFrame or some other way to bring image from URL as background of Video.
You could visually position your iframe behind the canvas using css. Perhaps like this:
<div class="container">
<canvas></canvas>
<iframe></iframe>
</div>
.container {
position: relative;
}
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}

MindAR JS: MINDAR.IMAGE.MindARThree is not a constructor

I am learning Web AR development using MindAR, https://hiukim.github.io/mind-ar-js-doc/#:~:text=MindAR%20is%20an%20opensource%20web,are%20written%20for%20AFRAME%20integration. I am following a tutorial on Udemy, https://www.udemy.com/course/introduction-to-web-ar-development/learn/lecture/29791078#overview. But it is not working unfortunately for me but for the instructor.
This is my index.html page.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AR Research</title>
<script src="https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image.prod.js"></script>
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image-aframe.prod.js"></script>
<script src="./main.js" type="module">
</script>
<style>
html, body {
position: relative;
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
</body>
</html>
As you can see there is not much in the code. I basically embedded the required Mind AR libraries through CDN and add the css styling a little bit and reference my main.js file.
This is my main.js file.
document.addEventListener(`DOMContentLoaded`, () => {
const start = async () => {
// create the AR world and specify the marker
const mindarThree = new window.MINDAR.IMAGE.MindARThree({
container: document.body,
imageTargetSec: './groot.mind'
})
const { renderer, scene, camera } = mindarThree;
const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.5 });
const plane = new THREE.Mesh(geometry, material);
const anchor = mindarThree.addAnchor(0);
anchor.group.add(plane);
await mindarThree.start();
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
}
start();
})
When I run the code on the browser, I am getting the following error.
main.js:4 Uncaught (in promise) TypeError: MINDAR.IMAGE.MindARThree is not a constructor
at start (main.js:4:29)
at HTMLDocument.<anonymous> (main.js:24:5)
What is wrong with my code and how can I fix it?
I had the same problem. The solution is that a different MindAR package needs to be imported, the one that combines MindAR and Three.js:
Use this one:
https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image-three.prod.js
Not this one:
https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image.prod.js

Passing variables to child component doesn't update reactively when dragging parent

I have a component ("Mover.svelte") that's responsible for providing mouse-drag functionality to whatever component is in its ("Button.svelte" in this case.)
I'm trying to display the current X/Y position in the child object, which works, but doesn't update reactively as the object moves. (There's no error message, just not working as expected.)
I have tried a variety of ways to do this, including Svelte's context API, but none of the approaches I've tried seem to enable the reactivity.
REPL:
https://svelte.dev/repl/1d2b72dbf8aa465fa60b76f2e93fb0cc?version=3.49.0
app.svelte:
<script>
import Mover from "./Mover.svelte";
import Button from "./Button.svelte";
</script>
<Mover>
<Button />
</Mover>
Mover.svelte:
<script>
import { setContext } from 'svelte';
let moving = false;
export let x = 120;
export let y = 130;
setContext('x', x);
setContext('y', y);
function dragItem(e) {
if (moving) {
x += e.movementX;
y += e.movementY;
}
}
</script>
<div on:mousedown={() => moving = true} on:mouseup={() => moving = false} style="left: {x}px; top: {y}px;">
<slot />
</div>
<svelte:window on:mousemove={dragItem} />
<style>
div {
margin: 0;
padding: 0;
position: absolute;
border: 4px solid green;
}
</style>
Button.svelte:
<script>
import { getContext } from 'svelte';
let x = getContext('x')
let y = getContext('y')
</script>
<button>
X/Y should update on drag:
{x}, {y}
</button>
<style>
button {
border: 4px solid blue;
margin: 0;
padding: 12;
}
</style>
Any help would be greatly appreciated.
If you want to use a context, you have to use a store, as setting/getting the context is a one-time operation. Reactivity does not transfer between files except through props; stores can bridge that gap.
E.g. in Mover:
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
let moving = false;
export let x = 120;
export let y = 130;
const xStore = setContext('x', writable(x));
const yStore = setContext('y', writable(y));
// Synch the stores on change:
$: $xStore = x;
$: $yStore = y;
In Button add $ prefix to read from the stores:
<button>
X/Y should update on drag:
{$x}, {$y}
</button>
REPL
As alternative to contexts, using slot props would also be possible here.
In mover set the props on the slot:
<slot {x} {y} />
Pass them down in App:
<Mover let:x let:y>
<Button {x} {y} />
</Mover>
(Button then needs to declare props for x/y)
REPL

How can I embed 2 tableau dashboards in 1 website?

Currently, based on JavaScript, I have embedded one tableau dashboard in a website, code is shown below. Now, I want to embed 2 dashboard in a same website (this second dashboard is from another tableau publish web link like the one I am using), how could I do it? Meanwhile, I need to create something like 'switch' button to allow me switch to different dashboard contents on click. Can anyone please help me with this?
Current code:
<!DOCTYPE html>
<html>
<head>
<title>Basic Embed</title>
<script type="text/javascript"
src="https://public.tableau.com/javascripts/api/tableau-2.min.js"></script>
<script type="text/javascript">
function initViz() {
var containerDiv = document.getElementById("vizContainer"),
url = "https://us-east-1.online.tableau.com/t/zuar/views/Regional/GlobalTemperatures",
options = {
hideTabs: true,
onFirstInteractive: function () {
console.log("Run this code when the viz has finished loading.");
}
};
var viz = new tableau.Viz(containerDiv, url, options);
// Create a viz object and embed it in the container div.
}
</script>
</head>
<body onload="initViz();">
<div id="vizContainer" style="width:800px; height:700px;"></div>
</body>
</html>
You could use a simple js-toggle func and toggle between the dashboards or whatever content you want to have. Just replace the text-content of the respective divs with the viz-containers of your dashboards..
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript"
src="https://public.tableau.com/javascripts/api/tableau-2.min.js">
</script>
<script>
function toggle() {
var x1 = document.getElementById("dashboard1");
var x2 = document.getElementById("dashboard2");
var bl = document.getElementById("button1");
if (x1.style.display === "none") {
x1.style.display = "block";
x2.style.display = "none";
bl.firstChild.data = "To Data B";
} else {
x1.style.display = "none";
x2.style.display = "block";
bl.firstChild.data = "To Data A";
}
}
function initialize() {
var containerDiv1 = document.getElementById("dashboard1"),
url1 = "https://public.tableau.com/views/liveintegration1/Dashboard1",
options1 = {
hideTabs: true,
onFirstInteractive: function () {
console.log("Run this code when the viz has finished loading.");
}
};
var viz1 = new tableau.Viz(containerDiv1, url1, options1);
// Create a viz object and embed it in the container div.
var containerDiv2 = document.getElementById("dashboard2"),
url2 ="https://public.tableau.com/views/LearnEmbeddedAnalytics/SalesOverviewDashboard",
options2 = {
hideTabs: true,
onFirstInteractive: function () {
console.log("Run this code when the viz has finished loading.");
}
};
var viz2 = new tableau.Viz(containerDiv2, url2, options2);
}
</script>
<style>
#dashboard1 {
border: 1px solid green;
width:800px;
height:700px;
width: 100%;
margin-top: 20px;
}
#dashboard2 {
border: 1px solid blue;
width:800px;
height:700px;
width: 100%;
margin-top: 20px;
}
</style>
</head>
<body onload="initialize()">
<button id="button1" onclick="toggle()">To Data B</button>
<div id="dashboard1" style="width:800px; height:700px;"></div>
<div id="dashboard2" style="width:800px; height:700px;"></div>
</body>
</html>
When inspecting the page you can now see the iframe being imported to the empty div:

How to dynamically change css style properties with Node and Electron

I've got the following problem: I want to access the css properties of styles.css inside Electron. The problem is that I can't use document.getElementsByClassName() because there is no document in Node. The desired behaviour is to change the color of one div once the q key is pressed.
This is my code:
index.js
const url = require('url');
const path = require('path');
const {app, BrowserWindow, globalShortcut} = require('electron');
let mainWindow;
app.on('ready', function(){
// Create new window
mainWindow = new BrowserWindow({backgroundColor: '#000000', fullscreen : true, frame : false});
// Load html in window
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes:true
}))
globalShortcut.register('Esc', () => {
app.quit();
});
globalShortcut.register('q', () => {
leftLight();
});
});
//This doesn't work
function leftLight() {
var element = ;
element.style["background-color"] = "yellow";
}
index.html
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=blocktext>3:00</h2></div>
</body>
</html>
styles.css
.rect_green {
display: flex;
align-items: center;
height: 400px;
width:60%;
background-color: green;
position:relative;
top:100px;
text-align: center;
}
.rect_red {
display: flex;
align-items: center;
height:400px;
width:60%;
background-color: red;
position:relative;
top:120px;
float:right;
}
.crono {
display: flex;
align-items: center;
height:300px;
width:40%;
background-color: beige;
position:fixed;
left: 50%;
bottom : 50px;
transform: translate(-50%, 0px);
margin: 0 auto;
}
.blocktext {
margin-left: auto;
margin-right: auto;
font-family: "Palatino", Times, serif;
font-size: 180px;
}
Edit
After the modifications suggested by Gr8Miller (still no communication):
index.html
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=blocktext>3:00</h2></div>
</body>
<script type="text/javascript">
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
//var element = document.getElementsByClassName("rect_green");
//element.style["background-color"] = "yellow";
console.log("q pressed in html file");
});
</script>
</html>
And index.js
const url = require('url');
const path = require('path');
const {app, BrowserWindow, globalShortcut, ipcMain, webContents} = require('electron');
let mainWindow;
app.on('ready', function(){
// Create new window
mainWindow = new BrowserWindow({
backgroundColor: '#000000',
fullscreen : true,
frame : false,
icon : __dirname + "/res/icon.jpg",
webPreferences: {
nodeIntegration : true
}
});
// Load html in window
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes:true
}))
globalShortcut.register('Esc', () => {
app.quit();
});
globalShortcut.register('q', () => {
leftLight();
});
});
function leftLight() {
mainWindow && mainWindow.webContents.send('key-pressed-q');
console.log("Sending q pressed to html...");
}
Possible duplicate of this.
Try to create another js file containing your code and load it as a script from your html document.
Add this to your index.html:
<script type="text/javascript" charset="utf8" src="./pageScript.js"></script>
And create a separate pageScript.js file with the code you want:
window.onload = function () {
// your code here
function leftLight() {
var element = ;
element.style["background-color"] = "yellow";
}
// Also don't forget to call the function
leftLight();
}
View related tasks should be handled in render process but not main process.
in Electron, the entry js(index.js in your case) runs in main process (it acts manager of all browser windows it creates) and the browser window itself runs in render process. html elements and imported/embedded js "live"/"run" in browser windows(in render process), so document can only be directly accessed in render process.
in your case. the style change task should be done in render process:
send a message(e.g. key-pressed-q) to render process from the main process on key q is pressed:
change the style in render process on receiving message (key-pressed-q):
index.js
mainWindow = new BrowserWindow({
backgroundColor: '#000000',
fullscreen : true,
frame : false,
webPreferences: {
nodeIntegration: true
}});
...
function leftLight() {
mainWindow && mainWindow.webContents.send('key-pressed-q');
}
index.html
...
<script type="text/javascript">
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
console.log(e);
var element = ;
element.style.backgroundColor = "yellow";
});
</script>
...
Added in 2019-11-18
There are other errors need to be fixed in your code which is not related to electron but just html basics:
//var element = document.getElementsByClassName("rect_green");
//element.style["background-color"] = "yellow";
getElementsByClassName returns an array of Element(Array<Element>) but not a single Element.
element.style doesn't have a field named background-color, it should be backgroundColor.
the console.log in the render process won't print logs in the main process's console, it outputs to its hosting browser window's own console. if you want to check the log, you have to open the browser window's Devtools first.
// in your `index.js`
// Open the DevTools.
mainWindow.webContents.openDevTools(); // `console.log` in `index.html` output to its hosting browser window's own console.
// in your `index.html`
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
var element = document.querySelector(".rect_green");
element.style.backgroundColor = "yellow";
console.log("q pressed in html file"); // this won't output to the main process's console.
});
Change your index.html to this :
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=blocktext>3:00</h2></div>
<script>
function leftLight() {
const element = document.getElementsByClassName("yourclassname")
element[0].style.color = "red"
}
window.onkeydown = function(e) {
if (e.key == "q") {
leftLight()
}
}
</script>
</body>
</html>

Categories

Resources