I want to create a card, once clicked on, it rotates 180°.
So I want to create a card component, that has a plane for the back side and a plane for the front side.
Once clicked on the card-entity, it rotates both planes, so that is looks like the card is turning around.
So here's what I got so far:
custom component: Card, gets added X-times.
A cursor detects a mouseEnter, then I want to card to rotate.
On mouseLeave, the card rotates back to the original state.
So, how can I achieve this the best?
Multiple meshes in a custom component or something?
AFRAME.registerComponent('card', {
schema: {
material: {type:'selector'},
hoverMaterial: {type: 'selector'},
card: {type: 'selector'}
},
multiple: true,
element: null,
init: function () {
console.log("Card loaded");
var data = this.data;
this.el.setAttribute('material', 'src', data.material);
this.el.addEventListener('mouseenter', this.onMouseEnter.bind(this));
this.el.addEventListener('mouseleave', this.onMouseLeave.bind(this));
},
onMouseEnter: function() {
this.el.removeEventListener('mouseenter', this.onMouseEnter.bind(this));
this.el.setAttribute('material', 'src', this.data.hoverMaterial);
this.el.setAttribute('rotation', '0 180 0');
},
onMouseLeave: function () {
//this.el.addEventListener('mouseenter', this.onMouseEnter.bind(this));
this.el.setAttribute('material', 'src', this.data.material);
this.el.setAttribute('rotation', '0 0 0');
},
update: function (oldData) {
},
remove: function () {
this.el.removeObject3D('mesh');
}
});
Create a custom component that appends two child entities to itself with the appropriate orientations and materials - for front and back sides.
You could also add a transparent plane mesh to the component, to enable raycasting.
Reference to the HTML node is this.el and you can manipulate it like a regular HTML node.
Code sample:
AFRAME.registerComponent('card', {
// ...
addChildren: function () {
this.frontFace = document.createElement('a-entity');
this.backFace = document.createElement('a-entity');
// ...
this.el.appendChild(this.frontFace);
this.el.appendChild(this.backFace);
}
// ...
For the input use either a cursor component or the raycaster component itself. Note that you have to have a mesh on the component you are trying to use in raycasting.
Related
I'm building a music player and I want to give windows information like the current song's title and artist, and the album art in the popup in the attached picture. It currently display's the song name by changing the <title/> of the page.
Using Electron, HTML, and JavaScript, is this possible?
(Please ask if you need to see any code, I'm not sure what'd be helpful and what would just clutter the question so just ask)
assigned vars are:
songName - songs name,
albumName - albums name,
artistName - artists name,
res - album art,
lyrics - songs lyrics
Yes, this is posible.
You can do this using Media Session API.
Media Session API allows a web page to provide custom behaviors for standard media playback interactions, and to report metadata that can be sent by the user agent to the device or operating system for presentation in standardized user interface elements.
Plus, you can take control about play, pause, previous track and next track buttons.
You can read more about it in next link:
https://developer.mozilla.org/en-US/docs/Web/API/MediaSession
This is a little example:
var trackElement = document.getElementById("track_el");
// Make sure browser has Media Session API available
if ('mediaSession' in navigator) {
// Access to Media Session API
var ms = navigator.mediaSession;
// Create track info JSON variable
var trackInfo = {};
// Set track title
trackInfo.title = "Polaris";
// Set artist name
trackInfo.artist = "Downtown Binary & The Present Sound";
// Set album name
trackInfo.album = "Umbra";
// Set album art (NOTE: image files must be hosted in "http" or "https" protocol to be shown)
trackInfo.artwork = [
{ src: 'https://antonyhr.neocities.org/temp/polaris/polaris_album_art_96.jpg', sizes: '96x96', type: 'image/jpg' },
{ src: 'https://antonyhr.neocities.org/temp/polaris/polaris_album_art_128.jpg', sizes: '128x128', type: 'image/jpg' },
{ src: 'https://antonyhr.neocities.org/temp/polaris/polaris_album_art_192.jpg', sizes: '192x192', type: 'image/jpg' },
{ src: 'https://antonyhr.neocities.org/temp/polaris/polaris_album_art_256.jpg', sizes: '256x256', type: 'image/jpg' },
{ src: 'https://antonyhr.neocities.org/temp/polaris/polaris_album_art_384.jpg', sizes: '384x384', type: 'image/jpg' },
{ src: 'https://antonyhr.neocities.org/temp/polaris/polaris_album_art_512.jpg', sizes: '512x512', type: 'image/jpg' }
];
// Then, we create a new MediaMetadata and pass our trackInfo JSON variable
var mediaMD = new MediaMetadata(trackInfo);
// We assign our mediaMD to MediaSession.metadata property
ms.metadata = mediaMD
// And that will be all for show our custom track info in Windows (or any supported) Media Player Pop-Up
// If we need to customize Media controls, we must set action handlers (NOTE: It's not necessary to add all action handlers).
ms.setActionHandler('play', function() {
trackElement.play();
var trackInfoEl = document.getElementById("track_info_el");
trackInfoEl.textContent = "Track is playing.";
});
ms.setActionHandler('pause', function() {
trackElement.pause();
var trackInfoEl = document.getElementById("track_info_el");
trackInfoEl.textContent = "Track is paused.";
});
ms.setActionHandler('stop', function() { /* Code excerpted. */ });
ms.setActionHandler('seekbackward', function() { /* Code excerpted. */ });
ms.setActionHandler('seekforward', function() { /* Code excerpted. */ });
ms.setActionHandler('seekto', function() { /* Code excerpted. */ });
ms.setActionHandler('previoustrack', function() { /* Code excerpted. */ });
ms.setActionHandler('nexttrack', function() { /* Code excerpted. */ });
} else {
console.warn("Your browser doesn't have Media Session API");
}
body {background: #181818;}
p {color: #fff; font-family: "Verdana", "Roboto", "Century Gothic";}
<audio id="track_el" controls autoplay src="https://firebasestorage.googleapis.com/v0/b/tempfiles-2912.appspot.com/o/polaris_clip.mp3?alt=media&token=bcfc245e-dc89-4046-b287-b99046c786bf" type="audio/mp3"></audio>
<p id="track_info_el">Track is playing.</p>
<p>Once audio is playing, see Media Player Pop-Up.</p>
I'm not pretty sure how you can add Lyrics to Media Player Pop-Up, but I think everything else is covered ✅.
If this answer was helpful, please, don't doubt to vote up.
Have a nice coding :D
I am developing a game where I want my textured image to remain on the top of video player but currently video player always stays on the top of all views?
I am using the CocosCreator framework and typescript.
VideoPlayer, EditBox and WebView Component are appended in cc.game.container so these component will add as last child and These will appear on the top.
By following these steps you can change the order or appearance.
If you wan to put something like a video player Behind the canvas and want to
put other component above it, So first you will have to make your canvas transparent. You can do this by setting the flag of engine file "/engine/cocos2d/core/platform/CCMacro.js"
Change the flag ENABLE_TRANSPARENT_CANVAS to true
You need the change the ZOrder of canvas and video component class in the start of the script responsible for video player.
start: function () {
cc.director.setClearColor(new cc.Color(0, 0, 0, 0))
let videoElement = document.getElementsByClassName('cocosVideo')[0];
videoElement.style.zIndex = 2;
let gameCanvas = document.getElementsByClassName('gameCanvas')[0];
gameCanvas.style.position = 'relative';
gCanvas.style.zIndex = 4;
},
3.Here is the complete script to play a video over canvas.
cc.Class({
extends: cc.Component,
properties: {
video:{
default: null,
type: cc.VideoPlayer
}
},
// use this for initialization
onLoad: function () {
this.videoPlayer = this.node.getComponent(cc.VideoPlayer);
this.videoPlayer.node.on('ready-to-play', this.callback, this);
},
start: function () {
cc.director.setClearColor(new cc.Color(0, 0, 0, 0))
let videoElement = document.getElementsByClassName('cocosVideo')[0];
videoElement.style.zIndex = 2;
let gameCanvas = document.getElementsByClassName('gameCanvas')[0];
gameCanvas.style.position = 'relative';
gCanvas.style.zIndex = 4;
},
callback () {
console.log("video ready to play")
this.videoPlayer.play();
},
// called every frame
update: function (dt) {
},
});
Basically the problem is that after you rotate the camera, the points that are given as arguments in the callback for dragging are not what I expected. I'm guessing I have to Rotate the given points also but I just couldn't.
Can Someone explain what is going on, is this some kind of bug or what should I do in order the sprite to follow the mouse cursor?
Easiest way to explain the problem is to reproduce it:
1) Go to Phaser Example Runner
2) Copy- Paste this code:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
function create ()
{
var image = this.add.sprite(200, 300, 'eye').setInteractive();
this.cameras.main.setRotation(Math.PI);
image.on('pointerover', function () {
this.setTint(0x00ff00);
});
image.on('pointerout', function () {
this.clearTint();
});
this.input.setDraggable(image);
this.input.on('dragstart', function (pointer, gameObject) {
gameObject.setTint(0xff0000);
});
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
console.log(`x: ${dragX}, y: ${dragY}`);
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', function (pointer, gameObject) {
gameObject.clearTint();
});
}
3) Open the console, drag around the Eye and look at what coordinates are given.
4) If you remove line 24 (the rotation of the camera) Everything works as expected.
(The example is taken from Phaser 3 Official examples and a bit changed for the bug)
According to Phaser's API Documentation on the setRotation() method, the rotation given in radians applies to everything rendered by the camera. Unfortunately, your pointer isn't rendered by the camera so it doesn't get the same rotated coordinates. Not sure if this is a bug with the library or just a poorly documented exception, but I believe there is a workaround.
Create 2 variables to hold an initial position and a final position:
var image = this.add.sprite(200, 300, 'eye').setInteractive();
var initial = [];
var final = [];
Populate the initial position in your .on('dragstart') method:
this.input.on('dragstart', function (pointer, gameObject) {
initial = [
gameObject.x,
gameObject.y,
pointer.x,
pointer.y
];
gameObject.setTint(0xff0000);
});
Then, populate the final variable in your .on('drag') method:
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
final = [
gameObject.x, // not necessary but keeping for variable shape consistency
gameObject.y, // not necessary but keeping for variable shape consistency
pointer.x,
pointer.y
];
gameObject.x = initial[0] + (initial[2] - final[2]);
gameObject.y = initial[1] + (initial[3] - final[3]);
});
All we're doing here is keeping track of the change in pointer position and mimicking that change in our gameObject.
I have added a common Surface and a Scrollview (actually a FlexScrollView) to a SequentialLayout and then added the layout to my View as the following code shows:
function _createCameraView() {
var layoutViews = [];
this.cameraView = new View({
size: [undefined, this.options.footerSize]
});
var layout = new SequentialLayout({
size: [undefined, this.options.footerSize],
direction: Utility.Direction.Y
});
var backgroundSurface = new Surface({
size: [undefined, 120],
content: 'some content',
classes : ['photo-guide-content'],
properties: {
backgroundColor: 'transparent',
zIndex: 4
}
});
// detail list
var detailsList = new FlexScrollView({
layout: ListLayout,
direction: Utility.Direction.X, // set direction to horizontal
paginated: true,
//autoPipeEvents: true,
useContainer: true,
container : {
size: [undefined, 60],
properties : {
'z-index' : 5,
}
}
});
layoutViews.push(backgroundSurface);
layoutViews.push(detailsList);
layout.sequenceFrom(layoutViews);
this.cameraView.add(alignModifier).add(layout);
}
Then I've added a RenderController on another view and I'm using it to display my cameraView like this:
function _selectCamera() {
if (!this.cameraView) {
_createCameraView.call(this);
}
this.footerRender.hide(this.mapView);
this.footerRender.show(this.cameraView);
}
I know I don't need to call the hide method from the render on the function above, just the show method and it should swap the views with a smooth opacity transition, which is defined by default. Although when using this Sequential Layout I'm not getting this effect. I know it has to do with this layout because I've tried to switch the layout to a FlexibleLayout and a GridLayout, and it works fine with these two. So, my question is, why doesn't work with the SequentialLayout? I've also noticed it has a method called setOutputFunction(outputFunction), is there a way to use it so the layout uses the opacity returned from the RenderController or how can it be fixed some other way?
Try adding a getSize method to your view.
Something like this:
this.cameraView.getSize = function() {
return [undefined, undefined];
};
My auto YUI autocomplete zindex is off. How can I force the autocomplete DIV to the top.
Below I am using a standard template for YUI:
YAHOO.util.Event.onDOMReady(function(){
YUI().use("autocomplete", "autocomplete-filters", "autocomplete-highlighters", function (Y) {
var inputNode = Y.one('#name'),
tags = [
'css',
'css3',
'douglas crockford',
'ecmascript',
'html',
'html5',
'java',
'javascript',
'json',
'node.js',
'pie',
'yui'
],
lastValue = '';
inputNode.plug(Y.Plugin.AutoComplete, {
activateFirstItem: true,
minQueryLength: 0,
queryDelay: 0,
source: tags,
resultHighlighter: 'startsWith',
resultFilters: ['startsWith']
});
// When the input node receives focus, send an empty query to display the full
// list of tag suggestions.
inputNode.on('focus', function () {
inputNode.ac.sendRequest('');
});
// When there are new AutoComplete results, check the length of the result
// list. A length of zero means the value isn't in the list, so reset it to
// the last known good value.
inputNode.ac.on('results', function (e) {
if (e.results.length) {
lastValue = inputNode.ac.get('value');
} else {
inputNode.set('value', lastValue);
}
});
// Update the last known good value after a selection is made.
inputNode.ac.after('select', function (e) {
lastValue = e.result.text;
});
});
});
Simply to put the z-index in the css. Setting via JS used to be allowed, but as of YUI 3.4.0 it's a css-only flag (https://github.com/yui/yui3/blob/master/src/autocomplete/HISTORY.md).
The relevant CSS is (adjust your z-index as necessary):
.yui3-aclist { z-index: 100; }
PS., your YAHOO. line is from YUI2, so that is quite peculiar and definitely not a standard template.
By the time your callback in the YUI().use(...) section is called, the dom should be ready. No ondomready required.