Using Three.js with Meteor - meteor

I am trying to load the basic rotating cube example from the three.js getting started guide in to a basic meteor app:
http://threejs.org/docs/index.html#Manual/Introduction/Creating_a_scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.CubeGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var render = function () {
requestAnimationFrame(render);
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render(scene, camera);
};
I have added this code into a template js manager file of a new meteor app and then call the render function on the render of my template:
Template.hello.rendered = function() {
if (!this._rendered) {
this._rendered = true;
render();
}
}
My template
<template name="hello">
<head>
<title>My first Three.js app</title>
<style>canvas { width: 100%; height: 100% }</style>
</head>
<body>
</body>
</template>
I have added three.js from atomosphere via:
mrt add three
and it loaded the package successfully into my app
However, when I start meteor I am getting an error that THREE is undefined.
I have tried moving three.js into the lib folder and that didn't work because I get an error that 'self' is undefined. I think this should only load as a client library. I have tried putting it with the client folder and below my manager file, but that didn't seem to work either and it seemed hacky. It seems like meteorite installed packages should be loaded first, right? WHy isn't this available from my template manager?
Any help would be greatly appreciated. I am new to both meteor and three.js so I think getting this basic demo working would really open my eyes.
Thanks!

Use the bower package and add three js through that.
https://atmospherejs.com/package/bower
I find that a lot of the wrapper packages on atmosphere fall out of date, so the bower package is a nice solution for this sort of thing.

The THREE.js library should go into client/compatibility folder. The package you were trying to use is probably deprecated.
EDIT
You can also try wrapping your THREEJS code with Meteor.startup to ensure the work is only performed after all js files are loaded. The Meteor's file loading order has always been a headake.

I really appreciate the feedback!
I also tried Bower and got a reference error on a new project after installing threejs package via Bower after starting Meteor. I am not sure what the issue is there.
However, I think I identified the problem with using three.js in the client folder. THREE is defined with VAR so only has local file scope and was not available from my manager file. There is a newer atmosphere package that has modified this to use a global scope:
https://atmospherejs.com/package/three.js
This package works. I found that you can also use the newest min.three.js file directly in the client folder without installing a package if you add window.THREE = THREE; to the end of the file--giving the local THREE variable access to global scope.
Finally, it's worth mentioning that I had defined my 'var scene' and other three.js code as shown in my question outside of the if Meteor.isClient function. Since my three.js code was within the client folder and inaccessible to the server, the server was throwing this error.
I hope someone finds my pitfalls helpful. Meteor definitely requires a slightly new way of looking at a JS appilcation, but I think it will be really great. I am excited to move past this seemingly simple issue that has been driving me crazy.

Related

Loading local file inside an iframe in Electron

I've tried different approaches but all are problematic.
So first of all I was using webview, but as per electron documentation, this tag is undergoing major architectural changes and it's recommended to use iframe or other alternatives. Furthermore, the webview tag gives me a warning while used alongside VueJS that the component is not registered. I understand this component doesn't exist within HTML standards and is something specific to electron, so I am not sure how to tell Vue to ignore or recognize it in the use case of an electron app.
Coming to the iframe problem, approach one of loading the file directly via src, gives me the obvious error Not allowed to load local resource:. Turning off webSecurity though allows the file to load but I read it's not recommended to turn it off. I am not sure if there are specific use case where it's safe to turn it off or shouldn't be at all.
I decided to try via file protocol as I already have it in place. The protocol code:
protocol.registerFileProtocol('downloads', (request, callback) => {
const url = request.url.substring('downloads:///'.length)
const location = path.normalize(paths.downloads(url))
callback({ path: location })
})
Though when I load the file this way, the renderer process crash without errors. Is there something in addition to the above which would help loading local files via iframe?
Edit 1
My use case is the following: I have a typical entry point to an index.html which contains code for a VueJS app.
if (app.isPackaged) {
window.loadFile(join(__dirname, '../renderer/index.html'))
} else {
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
window.loadURL(url)
window.webContents.openDevTools()
}
Inside that VueJS app, I require to list html files from a directory. I am able to achieve so via webview but I have tried to move away from it for the reason mentioned above. I tried using iframe but encountered issues as well. If there's a setting that doesn't turn off all security and allows me to load the file via iframe, that would be ideal.
This is kind of the reverse of this question where they're using an iframe, running into the "not allowed to load local resource" and being told to use a <webview> instead.
The <webview> docs list BrowserView as another alternative which is what I would recommend here. That should be much easier to work with than an iframe.
const { app, BrowserView, BrowserWindow } = require('electron')
app.whenReady().then(() => {
const win = new BrowserWindow()
const view = new BrowserView()
win.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
view.webContents.loadFile('<yourFile>')
})
Even though it is not recommended to use webview tag, I decided to go forward as it's the only thing that works for me. The only issue then was this error where Vue does not recognize the tag. To work around that error/warning, I had to update my vite.js config:
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === 'webview'
}
}
}),
// ...

Using third party javascript package with Meteor

I'm working with Meteor at the moment, and I'm trying to make it look more 'real timey' by adding transitions to numbers as they change. The best third party package I can see for that is http://github.hubspot.com/odometer/.
I'm having trouble getting the package to work in Meteor to update comment numbers on an item.
I've tried putting the javascript into client/compatibility as per the meteor docs: http://docs.meteor.com/#structuringyourapp, but no joy.
The other issue might be that the package uses CSS transitions, which would mean that a re-rendering of the template around the number that is updating would prevent the transition from occurring. To try and fix this problem, I used {{#isolate}} around the number, but that didn't work either.
Has anyone got any other ideas on what else in meteor might be getting in the way?
I think you should try {{#constant}} instead of {{#isolate}}. Also note, that the "constant" part of your template will no longer be reactive, so you'll have to update it manually. Supposing that you have a template
<template name="myTemplate">
{{#constant}}
<span class="odometer"></span>
{{/constant}}
</template>
you will need to do something like this:
Template.myTemplate.rendered = function () {
var node = this.find('.odometer');
Deps.autorun(function () {
node.innerHtml = MyCollection.find({}).count();
});
}

Meteor+Blade template variables catch 22

I'm trying to use a variable in my Blade template, but I always get
ReferenceError: files is not defined
My understanding is that the proper way to pass a variable to a template is something like this (client/ceres.js):
Meteor.startup(function() {
Files = new Meteor.Collection('files');
Template['files'].files = function() {
return Files.find();
}
});
(Copying from the "todos" example)
And then I should be able to use it in my template, views/files.blade:
ul
foreach files as file
li= file.filename
But I guess the variable is passed to the template too late? But if I take my JS out of Meteor.js then Template isn't defined.
So I don't get it. Either my template doesn't exist, or the variable doesn't exist, and it always crashes. How do I pass a simple variable along?
Same error with this:
ul
- for(var i=0; i<files.length; ++i)
li= files[i].filename
This is a known issue with Meteor that is actively being worked on.
The problem is that Meteor prevents smart packages from specifying the load order of files. See issue here.
Because of this issue, it is possible that your client-side JavaScript will run before the templates are loaded. (There is a hack in Meteor that ensures Handlebars templates load before your custom code) For example, Template.foo.helperName = function() { ... } will fail if Template.foo has not yet been defined.
Check the generated HTML (view source) for the initial page load to see if your client-side JavaScript code is loading before the template is defined. If so, you may get an Error like:
TypeError: Cannot set property 'helperName' of undefined`
To workaround this issue, try putting your client-side code in a folder with a different name. I believe that Meteor currently sorts files alphabetically when determining the load order. See the troubleshooting section on this page for more information.
A similar workaround is to utilize Meteor.startup when adding view helpers to your views. That is, you can wrap your Template.foo.helperName = ... stuff in a Meteor.startup call. If you are using a body.blade template, though, you can end up with the opposite problem (i.e. the "catch 22") in which your body.blade template starts rendering before view helpers get setup. In this case, you can get errors since those helpers/variables are not yet defined. The solution here is to avoid using body.blade templates and only render the initial template once all view helpers have been loaded (i.e. at the end of your Meteor.startup routine).
At any rate, all of these workarounds are rather lame. :( But, alas! These issues should be fixed soon.
As soon as Meteor fixes the issue described above, I will modify the Blade smart package to enforce the load order of compiled templates. My apologies for the confusion.
Turns out you can't include files that use Template variables either. i.e., you can't use the include directive in Blade at all if you want to use variables in your template that haven't been initialized by Meteor yet -- you have to insert your template via jQuery/JS after the DOM has loaded. Example:
views/body.blade:
.container
h1 Page Title
#content
views/files.blade:
ul
foreach files as file
- console.log(file);
li= file.filename
client/main.js:
Files = new Meteor.Collection('files');
Template.files.files = function() {
return Files.find();
};
$(function() {
$('#content').html(Meteor.render(Template.files));
});

Setting window dimensions in TideSDK

I am looking to add a small splash screen at the launch of an app I am working on to show licence and version info along with details of some client specific info. Having set my default window dimensions etc in tiapp.xml I am now looking at how to override these on a specific user window (namely index.html).
I've had a dig through the API and thought I had found what I was looking for, but on testing it doesn't do anything to the window dimensions. I know it will be my doing (I'm a php man trying desperately to learn javascript!) but can someone have a look at my code and point out the error of my ways...
In the of my index.html I have the following code:
<script>
var Ti = Titanium;
var window = Ti.UI.currentWindow;
window.setHeight(250);
window.setWidth(500);
</script>
I'm guessing I've either messed up my javascript or TideSDK syntax??
you have used the reserved variable "window", which belongs to JavaScript and can't be overwritten.
The following code works for me:
var Ti = Titanium;
var this_window = Ti.UI.getCurrentWindow();
setTimeout(function(){
this_window.setHeight(250);
this_window.setWidth(500);
}, 3000);
If you want to learn to write good JavaScript, I can give you a couple of book recommendations.

How to load a js file with javascript

I want to get over a nasty problem that shows up yesterday during a demo to a client. We're using jquery, loading it from google api. But yesterday, our ISP begin to cause some problems, and didn't load jq.js properly.
So, what I really want is to load a local file from the server if google api has an extrange behaviour (not that it's going to happen often, but at least doing local demos we won't get harmed again).
I know that <script type="txt/javascript" src="googleapi"> somejs </script> executes somejs when file in src doesn't load, but don't know any way to get the file load there.
Thanks in advance
inside you can put the following lines:
var localScript = document.createElement("script");
localScript.type = "text/javascript";
localScript.src = "localJQ.js";
document.body.appendChild(localScript);
Teaching granny to suck eggs possibly, but why not just use a local copy on the demo machine always?
Edit :
I was a bit jumpy. The solution given by peirix is correct. I'll just use his code, to not let the wrong solution pollute this area. You can do
var localScript = document.createElement("script");
localScript.type = "text/javascript";
localScript.src = "localJQ.js";
document.body.appendChild(localScript);
to load javascript dynamically.
Even this is prone to race conditions, however, if there are scripts in your page that depend on this script. Use with care.
This presentation - Even Faster Websites - elaborates on more techniques to load external files through javascript.
This isn't directly answering your question, but JQuery's creator John Resig has an interesting blog post about executing the script tag contents when there is also a src attribute.

Resources