I'm just starting to use Laravel (v 8.x) Mix in a project, and am finding it frustrating to implement Javascript from node modules.
To begin, I have this in my webpack.mix.js:
mix.js('node_modules/mxgraph/javascript/mxClient.min.js', 'public/js');
mix.js('resources/js/*.js', 'public/js').postCss('resources/css/app.css', 'public/css', [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
]).version();
Next, my app.js contains the following:
import Canvas from './canvas';
require('mxgraph');
const canvas = new Canvas();
...which imports canvas.js:
export default class Canvas {
constructor() {
this.container = document.getElementById('graphContainer');
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is not supported.
alert('Browser is not supported!');
}
.
.
.
}
}
...and in the Scripts section of my Blade layout:
<script src="{{ mix('js/mxClient.min.js') }}" defer></script>
<script src="{{ mix('js/app.js') }}" defer></script>
When I load the page, I get the following error in the console:
Uncaught ReferenceError: mxClient is not defined
at new Canvas (app.js:3866)
at Module../resources/js/app.js (app.js:3813)
at __webpack_require__ (app.js:114081)
at app.js:114143
at app.js:114149
var mxClient is definitely present in mxClient.min.js, and the reference to it in Canvas occurs after it's loaded.
I've tried a bunch of variations, and nothing seems to work. Any guidance would be greatly appreciated.
After a lot more poking and searching around, I found a method that works. It was based on what I found here:
https://www.programmersought.com/article/85491421858/
My implementation seems a bit clunky, but it does work, and I can now continue the project I'm trying to develop with mxGraph.
Thus, I no longer pull in mxgraph explicitly in webpack.mix.js, so it now reverts to the Laravel default:
mix.js('resources/js/*.js', 'public/js').postCss('resources/css/app.css', 'public/css', [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
]).version();
I've also removed require('mxgraph'); from app.js, so it just looks like this:
import Canvas from './canvas';
const canvas = new Canvas();
My canvas.js now looks like this:
import mx from 'mxgraph';
const mxgraph = mx({
mxImageBasePath: './src/images',
mxBasePath: './src'
});
window.mxGraph = mxgraph.mxGraph;
window.mxGraphModel = mxgraph.mxGraphModel;
window.mxEvent = mxgraph.mxEvent;
window.mxEditor = mxgraph.mxEditor;
window.mxGeometry = mxgraph.mxGeometry;
window.mxRubberband = mxgraph.mxRubberband;
window.mxDefaultKeyHandler = mxgraph.mxDefaultKeyHandler;
window.mxDefaultPopupMenu = mxgraph.mxDefaultPopupMenu;
window.mxStylesheet = mxgraph.mxStylesheet;
window.mxDefaultToolbar = mxgraph.mxDefaultToolbar;
const {mxGraph, mxClient, mxEvent, mxCodec, mxUtils, mxConstants, mxPerimeter, mxRubberband} = mxgraph;
export default class Canvas {
constructor() {
let container = document.getElementById('graphContainer');
if (typeof(mxClient) !== 'undefined') {
this.draw(container);
}
}
draw (container) {
if (! mxClient.isBrowserSupported())
{
// Displays an error message if the browser is not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Disables the built-in context menu
mxEvent.disableContextMenu(container);
// Creates the graph inside the given container
var graph = new mxGraph(container);
// Enables rubberband selection
new mxRubberband(graph);
// Gets the default parent for inserting new cells. This
// is normally the first child of the root (ie. layer 0).
var parent = graph.getDefaultParent();
// Adds cells to the model in a single step
graph.getModel().beginUpdate();
try
{
var v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
var e1 = graph.insertEdge(parent, null, '', v1, v2);
}
finally
{
// Updates the display
graph.getModel().endUpdate();
}
}
}
}
Most of the code in the draw() method is taken from an mxGraph "Hello World" demo (https://jgraph.github.io/mxgraph/docs/manual.html).
Many thanks to #codedge for the time you spent helping me!
So, after playing around I found what you can do.
Assemble one file
Put mxClient.min.js and app.js into one file by doing
mix.js(
[
"resources/js/app.js",
"node_modules/mxgraph/javascript/mxClient.min.js",
],
"public/js"
).postCss('resources/css/app.css', 'public/css', [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
]).version();
Import mxGraph and Canvas
In your app.js you can put that
import "./canvas";
require("mxgraph");
const canvas = new Canvas();
and npm run dev runs without issues.
Update
I found a (maybe easier) option. Don't include mxClient in your webpack.mix.js, only your app.js is needed.
// app.js
window.mxClient = new require("mxgraph")().mxClient;
let isBrowserSupported = mxClient.isBrowserSupported();
console.log(isBrowserSupported);
Related
I have installed react-planner that uses three.js as a package into a react app and managed to get it to render etc. The planner uses a catalog that contains several items that have their own respective textures, I have saved this alongside my src folder in the main app as well according to how their demo is set up.
When loading the app in the react app it doesn't load the textures or any images and leaves the elements without the required textures, instead its a simple gray colour in the case of the wall.
However when I run the package by itself through its own webpack config it does manage to load all the required textures and files.
This is the code for the wall:
import {ElementsFactories} from 'react-planner';
const info = {
title: 'wall',
tag: ['wall'],
description: 'Wall with bricks or painted',
image: require('./wall.png'),
visibility: {
catalog: true,
layerElementsVisible: true
}
};
const textures = {
bricks: {
name: 'Bricks',
uri: require('./textures/bricks.jpg'),
lengthRepeatScale: 0.01,
heightRepeatScale: 0.01,
normal: {
uri: require('./textures/bricks-normal.jpg'),
lengthRepeatScale: 0.01,
heightRepeatScale: 0.01,
normalScaleX: 0.8,
normalScaleY: 0.8
}
},
};
export default ElementsFactories.WallFactory('wall', info, textures);
This is the part of my wall-factory-3d.js that I presume takes in the texture and is what I presume responsible for applying the texture to the wall object:
const applyTexture = (material, texture, length, height) => {
let loader = new TextureLoader();
if (texture) {
material.map = loader.load(texture.uri);
material.needsUpdate = true;
material.map.wrapS = RepeatWrapping;
material.map.wrapT = RepeatWrapping;
material.map.repeat.set(length * texture.lengthRepeatScale, height * texture.heightRepeatScale);
if (texture.normal) {
material.normalMap = loader.load(texture.normal.uri);
material.normalScale = new Vector2(texture.normal.normalScaleX, texture.normal.normalScaleY);
material.normalMap.wrapS = RepeatWrapping;
material.normalMap.wrapT = RepeatWrapping;
material.normalMap.repeat.set(length * texture.normal.lengthRepeatScale, height * texture.normal.heightRepeatScale);
}
}
};
The catalog isn't registering the JPG and the PNG files for the textures nor the elements.
This is what I see when I run the app using its own webpack
And this is what I see when I use it as a package, it is getting the planner-element.js file but not registering the textures or any images from the catalog folder for some reason?
What should I look for or investigate? The main app is a standard react app, the package is using older webpack and is a class based app that has no maintainers anymore so its not possible for me to get help from them unfortunately.
I am wondering if it is loading the URL wrong for the textures/images?
Any pointers would be appreciated, thanks!
Due to the environment I have to implement my code into, I can't use 'import'. I'm able to get the library I need just fine but I'm running into the following error when trying to use it.
"jQuery.Deferred exception: videojs is not defined ReferenceError: videojs is not defined"
I've found workarounds for videoJS that require using something like the following
import videojs from 'video.js';
window.videojs = videojs;
require('videojs-http-source-selector');
Unfortunately that won't work since import is out of the question. Is there a way to work around this?
Below is my code in question. I use the ScriptLoader to load the library by file path in my solution.
/*This JS is meant to create an HMTL video component, then stream JSVideo/HLS to it. It's integrated in a no-code/low-code solution and is very particular about structure.*/
function MyControl() { };
MyControl.prototype.initialize = function(host, component){
/*Here we load our libraries. We must load it this way because of the way we integrate javascript into the no code solution*/
$.when(
$DP.ScriptLoader.LoadScript(
virtualPath + "/scripts/FormControls/jsFiles/video.js"
)
)
// Here we assign 'host' to a variable on this control,
// so that we can access it later:
this.host = host;
// Create and configure the video elemnt:
var video = document.createElement('video-js');
video.setAttribute("id","videoplayer");
video.setAttribute("class","video-js vjs-default-skin");
// Add the video to the 'host' jquery object:
host.append(video);
//Gets the source and plays on our video element previosuly made.
const player = videojs('videoplayer', {
html5: {
hls: {
withCredentials: true
}
},
controls : true,
fluid: true,
controlBar: {
children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'seekToLive', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'settingMenuButton', 'qualitySelector','fullscreenToggle']
},
preload : 'auto',
poster : '',
});
player.src({
src: 'http://demo.unified-streaming.com/video/tears-of-steel/tears-of-steel.ism/.m3u8',
type: 'application/x-mpegURL'
});
player.play();
};
MyControl.prototype.resize = function(height, width) {
if (this.host && height && width && (height > 0) && (width > 0)) {
this.host.css({ width: width, height: height });
}
};
MyControl.prototype.setValue = function(data){
// This control has no inputs, so no code is necessary here.
};
MyControl.prototype.getValue = function() {
// Output data should be an object with properties that match the
return {
};
};
how about
var videojs = require(/path/+'video.js');
I have some webfonts that get loaded via Webfontloader load like this ...
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js"></script>
<script>
WebFont.load({
custom: {
families: ['BlenderProBook', 'BlenderProMedium']
}
});
</script>
And it works great when first loading the page ... Problems is, when refreshing the page it only retrieves the cached fonts when requested in html and not before my ReactJS app runs (when the Webfontloader normally gets them). This is too late for me, because I'm using them in pre-generated SVG.
Is there a way to force it to get the uncached fonts each time? Or better, load the cached fonts at the correct time.
I found a solution to this, which I will take as my answer if no-one else can supply a better one.
My font is being used extensively in the app to calculate text widths and therefore layout ... (which is why it needs to be loaded before the app runs), so I decided to use the text width code as a test to block the app from running.
Here's the code
var testFont = require('./components/svgText.js');
var timer = setInterval(function () {
var test = (testFont('TEST', 'BlenderProBook', 20).width === 39.75);
if (test) {
clearInterval(timer);
Router.run(routes, function (Handler) {
React.render(<Handler/>, document.body);
});
}
}, 50);
where svgTest.js uses Raphael SVG to generate SVG text and retrieve the measurements like this:-
renderedTextSize = function (string, font, fontSize) {
var paper = Raphael(0, 0, 0, 0);
paper.canvas.style.visibility = 'hidden';
var el = paper.text(0, 0, string);
el.attr('font-family', font);
el.attr('font-size', fontSize);
var bBox = el.getBBox();
paper.remove();
return {
width: bBox.width,
height: bBox.height
};
};
module.exports = renderedTextSize;
This seems to work quite nicely, even though it feels a bit hacky to be testing against a magic number (width).
I am new to metro style programming so I am following a tutorial here http://www.sitepoint.com/creating-a-simple-windows-8-game-with-javascript-game-basics-createjseaseljs/ for games using createJS. Here the code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>game</title>
<!-- WinJS references -->
<link href="//Microsoft.WinJS.1.0.RC/css/ui-dark.css" rel="stylesheet" />
<script src="//Microsoft.WinJS.1.0.RC/js/base.js"></script>
<script src="//Microsoft.WinJS.1.0.RC/js/ui.js"></script>
<!-- game references -->
<link href="/css/default.css" rel="stylesheet" />
<script src="/js/default.js"></script>
<script src="/js/CreateJS/easeljs-0.5.0.min.js"></script>
<script src="/js/CreateJS/preloadjs-0.2.0.min.js"></script>
</head>
<body>
<canvas id="gameCanvas"></canvas>
</body>
</html>
and here's javascript code
// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232509
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
WinJS.strictProcessing();
function initialize() {
canvas = document.getElementById("gameCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
context = canvas.getContext("2d");
preload = new PreloadJS();
preload.onComplete = prepareGame;
var manifest = [
{ id: "screenImage", src: "images/Backgrounds/gameplay_screen.png" },
{ id: "redImage", src: "images/Catapults/Red/redIdle/redIdle.png" },
{ id: "blueImage", src: "images/Catapults/Blue/blueIdle/blueIdle.png" },
{ id: "ammoImage", src: "images/Ammo/rock_ammo.png" },
{ id: "winImage", src: "images/Backgrounds/victory.png" },
{ id: "loseImage", src: "images/Backgrounds/defeat.png" },
{ id: "blueFire", src: "images/Catapults/Blue/blueFire/blueCatapult_fire.png" },
{ id: "redFire", src: "images/Catapults/Red/redFire/redCatapult_fire.png" },
];
preload.loadManifest(manifest);
stage = new Stage(canvas);
}
function prepareGame() {
bgImage = preload.getResult("screenImage").result;
bgBitmap = new Bitmap(bgImage);
bgBitmap.scaleX = SCALE_X;
bgBitmap.scaleY = SCALE_Y;
stage.addChild(bgBitmap);
stage.update();
}
function gameLoop() {
}
function update() {
}
function draw() {
}
var canvas, context, stage;
var bgImage, p1Image, p2Image, ammoImage, p1lives, p2lives, title, endGameImage;
var bgBitmap, p1Bitmap, p2Bitmap, ammoBitmap;
var preload;
// Current Display Factor. Because the orignal assumed a 800x480 screen
var SCALE_X = window.innerWidth / 800;
var SCALE_Y = window.innerHeight / 480;
var MARGIN = 25;
var GROUND_Y = 390 * SCALE_Y;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
}
args.setPromise(WinJS.UI.processAll());
}
};
app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state
// that needs to persist across suspensions here. You might use the
// WinJS.Application.sessionState object, which is automatically
// saved and restored across suspension. If you need to complete an
// asynchronous operation before your application is suspended, call
// args.setPromise().
};
document.addEventListener("DOMContentLoaded", initialize, false);
app.start();
})();
Now the problem is that whenever I try to compile the code it gives me the Preload is undefined error. I do not understand the reason I have included the script in HTML and file in my project. Would somebody please help this problem has been killing me for the last hour and I just want to make a simple game.
The reason for this is that the libraries have been placed under the "createjs" namespace. In order to instantiate the PreloadJS object, please do the following:
var preload = new createjs.PreloadJS();
That should get you back to happy path.
(Tried on Feb 2013, after PreloadJS 0.3.0 released)
I just tried the tutorial from sitepoint.com (http://www.sitepoint.com/creating-a-simple-windows-8-game-with-javascript-game-basics-createjseaseljs/), using the latest versions of EaselJS and PreloadJS, but I couldn't get the code to run without errors at first.
The new version of PreloadJS (v0.3.0) has renamed the old PreloadJS class to LoadQueue.
See the documentation at the links below:
http://www.createjs.com/Docs/PreloadJS/classes/PreloadJS.html
http://www.createjs.com/Docs/PreloadJS/classes/LoadQueue.html
But simply using the new class while using v0.3.0 doesn't fix the project. The newer version has additional incompatibilities (e.g. image.width is not valid) which can probably be fixed by trial and error.
I had to download the older PreloadJS v0.2.0:
https://github.com/CreateJS/PreloadJS/tags
IN A NUTSHELL: If I use the SitePoint tutorial, I have to stick with PreloadJS v0.2.0 and also use the createjs namespace.
NOTE: The sample project mentioned in a previous response doesn't work either, because its WinJS references are invalid. Adding the reference manually doesn't seem to work either.
https://github.com/dave-pointbypoint/CatapultGame
Move your default.js in the HTML to be after the preloadjs & easel code.
This is because the order of inclusion is important to JavaScript
I've been working on adapting arbor.js to use images.
However, being a relative JS noob what I have is totally un-optimised.
As far as I can tell, the way I've set it up is recreating the image object for every image and every frame, resulting in tons of flicker.
Can anyone suggest a way to move the new Image() stuff out of the redraw function into the initiation? As far as I know this is a basic OOP issue, but totally stuck.
Thanks!
Pastebin of where I'm up to on the output script
Current status.
Apologies all! There's a few steps. I'll highlight the key stages, the rest is from the tutorial.
First, add the relevant information to your JSON, for example:
nodes:{
innovation:{ 'color':colour.darkblue,
'shape':'dot',
'radius':30,
'image': 'innovation.png',
'image_w':130,
'image_h':24,
'alpha':1 },
participation:{ 'color':colour.purple,
'shape':'dot',
'radius':40,
'image':'participation.png',
'image_w':130,
'image_h':24,
'alpha':1 },
...
Cache all your images when the thing loads.
init:function(system){
// Normal initialisation
particleSystem = system
particleSystem.screenSize(canvas.width, canvas.height)
particleSystem.screenPadding(25, 50)
that.initMouseHandling()
// Preload all images into the node object
particleSystem.eachNode(function(node, pt) {
if(node.data.image) {
node.data.imageob = new Image()
node.data.imageob.src = imagepath + node.data.image
}
})
...
Then, for moving the images themselves...
particleSystem.eachNode(function(node, pt){
...
// Image info from JSON
var imageob = node.data.imageob
var imageH = node.data.image_h
var imageW = node.data.image_w
...
// Draw the object
if (node.data.shape=='dot'){
// Check if it's a dot
gfx.oval(pt.x-w/2, pt.y-w/2, w,w, {fill:ctx.fillStyle, alpha:node.data.alpha})
nodeBoxes[node.name] = [pt.x-w/2, pt.y-w/2, w,w]
// Does it have an image?
if (imageob){
// Images are drawn from cache
ctx.drawImage(imageob, pt.x-(imageW/2), pt.y+radius/2, imageW, imageH)
}
}else {
// If none of the above, draw a rectangle
gfx.rect(pt.x-w/2, pt.y-10, w,20, 4, {fill:ctx.fillStyle, alpha:node.data.alpha})
nodeBoxes[node.name] = [pt.x-w/2, pt.y-11, w, 22]
}
...