I would like to make responsive flat panel - on a mobile device should be in full screen like there
https://tour.croptrust.org/
Is there a possibility to achieve that in react-360.
Here is my example https://gstuczynski.com/tour/plaszow/ - try to hit loupe -> on the desktop looks good but how to make it responsive?
Here is my code
https://github.com/gstuczynski/plaszow-cc-virtual-tour
If you're looking to set your surface size based on whether user is on mobile or desktop, I'd approach this by building a NativeModule to check if the device is mobile on componentDidMount() and using the built in resize() function to adjust your surface sizes.
First add a module in client.js
function init(bundle, parent, options = {}) {
const r360 = new ReactInstance(bundle, parent, {
nativeModules: [
//new module added here
ctx => new SurfaceModule(ctx),
],
fullScreen: true,
...options,
});
}
In your case you already have vars for the surfaces - but remove const to expose them to your NativeModule:
infoPanel = new Surface(1440, 850, Surface.SurfaceShape.Flat);
mapPanel = new Surface(850, 800, Surface.SurfaceShape.Flat);
cylinderSurface = new Surface(4096, 720, Surface.SurfaceShape.Cylinder);
Next, create your class SurfaceModule and include a function to check for mobile and include a ref for the surface namecheckMobile(surfaceName)
import {Module} from 'react-360-web';
export default class SurfaceModule extends Module {
constructor(ctx) {
super('SurfaceModule');
this._ctx = ctx;
}
checkMobile(surfaceName, id) {
if (navigator.userAgent.match('add your match criteria here'))
{
let swidth = screen.width
let sheight = screen.height
surfaceName.resize(swidth, sheight);
this._ctx.invokeCallback(
id,
[swidth, sheight]
);
}
}
}
Finally in index.js run the function to check and adjust surface sizes.
With this setup you'll need to call the function for each Surface you want to check or you could rework to send multiple refs and perform an if/switch
You could also move the call out of componentDidMount() into another func
import {
NativeModules
} from 'react-360';
const SurfaceModule = NativeModules.SurfaceModule;
componentDidMount() {
SurfaceModule.checkMobile((swidth, sheight) => {
console.log(swidth);
console.log(sheight);
});
}
EDIT: Updated checkMobile() func & index.js to include callback
Related
Hope you all are doing best in you life!
I've newly intraction with 3d work. I'm making the 3d virtual showroom you can say 3d commerce page,where i need to put 3d Models. I want to add functionality of changing models color (limited colors) by users. like this: [https://codesandbox.io/s/v1fgk].(I'm using three js not react three fiber.)
THREE JS Version: "three": "^0.135.0",
The problem is that:
1) When i change the color of the mesh the details of the model are deleted.what can i do for this?
2) I face alot of lighting issues during this work.finally I'm using HDRI envornment instead of lighting in my models.
am i doing best?
Actual Model:
On Change Color:
Why my models's details are going to delete?
On Changing Color i'm doing this:
// const loader = new THREE.TextureLoader();
// const aoTexture = loader.load('textures/BottleModel/Model2/Material.002_Mixed_AO.png')
// const displacementTexture = loader.load('textures/BottleModel/Model2/base_Height.png')
// const metalicTexture = loader.load('textures/BottleModel/Model2/Material.002_Metallic.png')
// const roughnessTexture = loader.load('textures/BottleModel/Model2/Material.002_Roughness.png')
const BottleMesh=model.current.children.find((item)=>item.name==="cup_base")
BottleMesh.children.map((item)=>{
if(item.name==="Cylinder"){
item.material = new THREE.MeshStandardMaterial({
color: code,
// aoMap:aoTexture,
// metalnessMap : metalicTexture,
// displacementMap:displacementTexture,
// roughnessMap:roughnessTexture
})
}
})
CREATING MODEL AND ADDING ENVORNMENT:
export const createModel = (model, scene, path, setLoading,renderer) => { //model is a react ref, remember that
setLoading(true)
const dracoLoader = new DRACOLoader();
// // Specify path to a folder containing WASM/JS decoding libraries.
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
let loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
let generator = new THREE.PMREMGenerator(renderer);
new RGBELoader().setPath('/textures/hdri/') .load('dancing_hall_4k.hdr', function (texture) {
// let envmap =generator.fromEquirectangular(texture)
texture.mapping = THREE.EquirectangularReflectionMapping;
// scene.background = texture;
scene.environment = texture;
});
loader.load(path, function (gltf) {
model.current = gltf.scene;
model.current.position.x=0
model.current.position.y=-3
scene.add(model.current);
setLoading(false);
}, undefined, function (error) {
console.error(error)
});
}
I have a set of markers which I want to have visible or not on a React Google map.
In ESRI/ArcGIS maps you can create layers which can be turned on or off, but it does not seem any equivalent features exist in Google maps.
I suppose can give the markers a specific class and turn their visibility on or off, but I am concerned this may impact performance.
Any suggestions on a way forward?
Google Maps API does not support this kind of custom layers (refer official docs for a supported list of layers).
The following custom component demonstrates how to group markers and toggle its visibility
function MarkersGroup(props, context) {
const layersRef = useRef(null);
useEffect(() => {
const map = context[MAP];
let layers = null;
if (!layersRef.current) {
layers = new window.google.maps.MVCObject();
for (let name in props.groupData) {
for (let item of props.groupData[name].items) {
const markerProps = { position: { lat: item.lat, lng: item.lng } };
const marker = new window.google.maps.Marker(markerProps);
marker.bindTo("map", layers, name);
}
}
layersRef.current = layers;
} else layers = layersRef.current;
for (let name in props.groupData) {
if (props.groupData[name].visible) {
layers.set(name, map);
} else {
layers.set(name, null);
}
}
});
return null;
}
Notes:
google.maps.MVCObject class - is used to store layer(markers) group
layer visibility is toggled via MVCObject.set method
Here is a demo
Sometimes the game starts without waiting for all the assets to load. This happens most often with the spaceStation which is the largest sprite at 3372x700 px. When the game first boots, the spaceStation isn't loaded. But if you leave the game then come back to it without refreshing the page, it will be loaded the second time. I am using the Phaser game engine with the IonPhaser wrapper for React.
The game is hosted here so you can see what I mean: https://seanhetzel.github.io/star-runner/#/
Here is my code for the spaceStation sprite:
import spaceStation from "../assets/space-station-sprite-sheet.png";
// other stuff...
preload: function() {
// other stuff...
const spaceStationImg = new Image();
spaceStationImg.onload = () => {
this.textures.addSpriteSheet("spaceStation", spaceStationImg, {
frameWidth: 3372,
frameHeight: 700
});
};
spaceStationImg.src = spaceStation;
// other stuff...
}
// other stuff...
create: function() {
// other stuff...
const config = {
key: "lights",
frames: this.anims.generateFrameNumbers("spaceStation", {
start: 0,
end: 6
}),
frameRate: 3,
repeat: -1
};
this.anims.create(config);
this.spaceStation = this.impact.add
.sprite(1686, 350, "spaceStation")
.play("lights")
.setDepth(1);
// other stuff...
}
Am I not loading it correctly?
Thanks!
The Phaser 3 Loader should use the imported sprite instead of pointing directly to the relative path in the preload() function when using react.
import spaceStation from "../assets/space-station-sprite-sheet.png";
// other stuff...
this.load.spritesheet(
'spaceStation',
spaceStation, // <-- not '../assets/space-station-sprite-sheet.png'
{
frameWidth:3372,
frameHeight:700
}
);
Now it’s getting the sprite from import spaceStation from "../assets/space-station-sprite-sheet.png"; import instead of directly pointing to the relative path and using the Phaser 3 Loader.
I'm working on using a kendo inside of an angular 2 project.
Getting the widget set up correctly is no problem:
ngOnInit() {
let options = inputsToOptionObject(KendoUIScheduler, this);
options.dataBound = this.bound;
this.scheduler = $(this.element.nativeElement)
.kendoScheduler(options)
.data('kendoScheduler');
}
When that runs, the plugin modifies the DOM (and, to my knowleged, without modifiying the shadow DOM maintained by angular2). My issue is that if I want to use a component anywhere inside of the plugin, like in a template, Angular is unaware of it's existence and won't bind it.
Example:
public views:kendo.ui.SchedulerView[] = [{
type: 'month',
title: 'test',
dayTemplate: (x:any) => {
let date = x.date.getDate();
let count = this.data[date];
return `<monthly-scheduler-day [date]="test" [count]=${count}"></monthly-scheduler-day>`
}
}];
The monthly-scheduler-day class:
#Component({
selector: 'monthly-scheduler-day',
template: `
<div>{{date}}</div>
<div class="badge" (click)=dayClick($event)>Available</div>
`
})
export class MonthlySchedulerDayComponent implements OnInit{
#Input() date: number;
#Input() count: number;
constructor() {
console.log('constructed');
}
ngOnInit(){
console.log('created');
}
dayClick(event){
console.log('clicked a day');
}
}
Is there a "right" way to bind these components inside of the markup created by the widget? I've managed to do it by listening for the bind event from the widget and then looping over the elements it created and using the DynamicComponentLoader, but it feels wrong.
I found some of the details I needed in this thread: https://github.com/angular/angular/issues/6223
I whipped this service up to handle binding my components:
import { Injectable, ComponentMetadata, ViewContainerRef, ComponentResolver, ComponentRef, Injector } from '#angular/core';
declare var $:JQueryStatic;
#Injectable()
export class JQueryBinder {
constructor(
private resolver: ComponentResolver,
private injector: Injector
){}
public bindAll(
componentType: any,
contextParser:(html:string)=>{},
componentInitializer:(c: ComponentRef<any>, context: {})=>void):
void
{
let selector = Reflect.getMetadata('annotations', componentType).find((a:any) => {
return a instanceof ComponentMetadata
}).selector;
this.resolver.resolveComponent(componentType).then((factory)=> {
$(selector).each((i,e) => {
let context = contextParser($(e).html());
let c = factory.create(this.injector, null, e);
componentInitializer(c, context);
c.changeDetectorRef.detectChanges();
c.onDestroy(()=>{
c.changeDetectorRef.detach();
})
});
});
}
}
Params:
componentType: The component class you want to bind. It uses reflection to pull the selector it needs
contextParser: callback that takes the existing child html and constructs a context object (anything you need to initialize the component state)
componentInitializer - callback that initializes the created component with the context you parsed
Example usage:
let parser = (html: string) => {
return {
date: parseInt(html)
};
};
let initer = (c: ComponentRef<GridCellComponent>, context: { date: number })=>{
let d = context.date;
c.instance.count = this.data[d];
c.instance.date = d;
}
this.binder.bindAll(GridCellComponent, parser, initer );
Well your solution works fine until the component needs to change its state and rerender some stuff.
Because I haven't found yet any ability to get ViewContainerRef for an element generated outside of Angular (jquery, vanilla js or even server-side)
the first idea was to call detectChanges() by setting up an interval. And after several iterations finally I came to a solution which works for me.
So far in 2017 you have to replace ComponentResolver with ComponentResolverFactory and do almost the same things:
let componentFactory = this.factoryResolver.resolveComponentFactory(componentType),
componentRef = componentFactory.create(this.injector, null, selectorOrNode);
componentRef.changeDetectorRef.detectChanges();
After that you can emulate attaching component instance to the change detection cycle by subscribing to EventEmitters of its NgZone:
let enumerateProperties = obj => Object.keys(obj).map(key => obj[key]),
properties = enumerateProperties(injector.get(NgZone))
.filter(p => p instanceof EventEmitter);
let subscriptions = Observable.merge(...properties)
.subscribe(_ => changeDetectorRef.detectChanges());
Of course don't forget to unsubscribe on destroy:
componentRef.onDestroy(_ => {
subscriptions.forEach(x => x.unsubscribe());
componentRef.changeDetectorRef.detach();
});
UPD after stackoverflowing once more
Forget all the words above. It works but just follow this answer
I am trying to either override or extend an existing component - the Rally.ui.PercentDone component. I want to provide it with a portfolio item with all the data necessary to render it using the same color coding Rally native apps use (the Rally Health Color Calculator).
I really just need the function to pass the correct record. Here is my idea so far:
Ext.define('Custom.PercentDone', {
requires: ['Rally.ui.renderer.template.progressbar.PortfolioItemPercentDoneTemplate', 'Rally.util.HealthColorCalculator'],
extend : 'Rally.ui.PercentDone',
alias : 'widget.cpercentdone',
config: {
record: null
},
constructor: function(config) {
config = this.config;
this.renderTpl = Ext.create('Custom.renderer.template.progressbar.PercentDoneTemplate', {
calculateColorFn: Ext.bind(function(recordData) {
console.log('called my custom coloring fn');
var colorObject = Rally.util.HealthColorCalculator.calculateHealthColorForPortfolioItemData(config.record, config.percentDoneName);
return colorObject.hex;
}, this)
});
this.renderData = config;
this.mergeConfig(config);
this.callParent([this.config]);
}
});
var custom = Ext.create('Custom.PercentDone', {
record: item,
percentDoneName: 'PercentDoneByStoryPlanEstimate',
});
but my calculate color function is not being called instead of the default.
I got it to work by overriding:
var percentDoneByStoryCountEl = Ext.create("Rally.ui.PercentDone", {
record: item,
percentDoneName: "PercentDoneByStoryCount",
percentDone: item.PercentDoneByStoryCount
});
tpl = percentDoneByStoryCountEl.renderTpl;
tpl.calculateColorFn = function (recordData) {
var colorObject = Rally.util.HealthColorCalculator.calculateHealthColorForPortfolioItemData(item, "PercentDoneByStoryCount");
return colorObject.hex;
};
Ext.override(percentDoneByStoryCountEl, {
renderTpl: tpl
});
but I would like to figure out how to do it via extending the component instead.
Thanks for your help.