I'm working on my scrim component for A-Frame and want it to "turn off" when VR Mode is entered. Where in the component can I listen for the enter-vr event so I can remove animations?
I add animation settings for opacity on init of the component. I don't know where add a listener for enter-vr to remove the added animation.
init: function () {
var thisEl = this.el;
var data = this.data
var properties = 'property: material.opacity; from: 0; to: 1; dir: alternate; loop: true'
properties = properties.concat(properties,'; dur: ', data.durration, '; easing: ', data.easing)
thisEl.setAttribute('animation', properties)
document.querySelector('a-scene').addEventListener('enter-vr', remove())
},
remove: function () {
var thisEl = this.el;
thisEl.setAttribute('animation', '')
},
I expected the listener in the init function to call the remove function when enter-vr fires.
The method is not correctly attached. Try this instead:
document.querySelector('a-scene').addEventListener('enter-vr', this.remove.bind(this));
Your code is calling the function and passing the result to addEventListener not a reference to the function itself. I recommend reading about function declarations, statements and scoping in JavaScript for better understanding.
Also to remove a component use removeAttribute:
thisEl.removeAttribute(“animation”);
With setAttribute you are setting the properties to the default values not removing the component. Additional info in docs: https://aframe.io/docs/0.9.0/introduction/javascript-events-dom-apis.html#removing-a-component-with-removeattribute
Related
I'm trying to create a local clipping plane in AFrame, essentially this effect
https://threejs.org/examples/#webgl_clipping_intersection
To enable the clipping, you must have a reference to the renderer, and enable localClippingEnabled, like this:
renderer.localClippingEnabled = true;
In AFrame, the webGLRenderer is exposed through a the component renderer, but localClippingEnabled property is not supported. ie
<a-scene renderer="localClippingEnabled:true">
core:schema:warn Unknown property localClippingEnabled for component/system undefined.
AFRAME.registerComponent('matclipplane', {
schema:{
clipHeight:{type: 'number', default: 1}
},
init: function () {
let el = this.el;
let self = this;
self.scene = el.sceneEl.object3D;
let renderer = THREE.WebGLRenderer; // How to refernence the renderer?
renderer.localClippingEnabled = true;
I've attempted to reference it in a custom component, like this
let renderer = THREE.WebGLRenderer;
renderer.localClippingEnabled = true;
But it doesn't seem to be working. I'm not convinced this is a proper reference to the WebGLRenderer, as logging it in the console does not reveal the property localClippingEnabled.
From what I have seen in THREEjs examples, reference is always from a newly constructed renderer. So I tried that , and in the console, it looks right, with the localClippingEnable=true in the console, but still doesn't work, because (I don't think) this new renderer is doing the rendering.
How can I make this work?
Here is my glitch in progress.
https://glitch.com/~clipping-plane
According to the docs, you can access the renderer object as a scene property (sceneEl.renderer).
For example:
AFRAME.registerComponent('foo', {
init: function() {
console.log(this.el.sceneEl.renderer.localClippingEnabled)
this.el.sceneEl.renderer.localClippingEnabled = true
}
})
Check it out in this fiddle.
I have a fullCalendar widget created somewhere. I cannot change the code that initialize it. So I cannot add any callbacks in the first call.
Anything like this:
$(elem).fullCalendar({
complete: function () {
...
}
})
Actually creates a new fullCalendar instead of modifying the actual fullCalendar to change/add the complete callback.
Is there an other way to find out when events are loaded I was thinking about polling clientEvents but I realize that I could have no events in one month so I cannot expect the array to always have something in it.
By the way, it's fullCalendar 1.6.
You can define callbacks after the calendar object has been initialized, and to determine when all events have been rendered, use the eventAfterAllRender event. Here's how:
var calendar = $('#calendar').fullCalendar('getCalendar');
calendar.on('eventAfterAllRender', function(view) {
alert('all events rendered!');
});
Nevermind, this feature is only available starting in version 2.4.
Instead, you could poll the DOM for fullcalendar element existence, like this:
function checkForInit() {
if($(".fc-view").children().length) {
alert("initialized!");
} else {
setTimeout(function() {
checkForInit();
}, 10);
}
}
$(document).ready(function() {
checkForInit();
});
You can use eventAfterAllRender event, available from version 1.6:
$(elem).fullCalendar({
eventAfterAllRender: function (view) {
...
}
})
Ok I found a solution that seems to be working!
var waitPrint = true
function autoPrint() {
var elem = $('.fc')
var calendar = elem.data('fullCalendar')
console.log('Searching calendar')
if (calendar && waitPrint) {
waitPrint = false
console.log('Bund new event')
var _super = calendar.options.eventAfterAllRender
calendar.options.eventAfterAllRender = function (event, element, view) {
if (_super) {
_super.apply(this, arguments)
}
window.print()
}
} else if (waitPrint) {
setTimeout(autoPrint, 100)
}
}
autoPrint()
Here I'm polling for an element with the fc class. As soon as I find one, I check for the existence of the "data" named fullCalendar. If it returns a dict, then it means that the fullCalendar instance has been created. This is pretty much what Dane proposed for version 2.4 but this in 1.6 there is no getter. We have to get it ourselves. Luckily, it's stored in the data of the element and not in some other cryptic places.
Move on to the next step, fullCalendar isn't an eventEmitter in 1.x, but we still have access to options which seems to be just a reference to the options that were passed at first. I override the eventAfterAllRender. Call the method that was already defined if present and call my print method when it's done.
Technically from there, we can override almost any defined method from there. The only problem is that you have to do it faster than fullCalendar get initialized.
I believe that if we dig deeper, we could potentially patch the calendar library directly to remove the timing issues. Polling isn't very great.
Best Solution So far
var oldFullCalendar = $.fn.fullCalendar
function newFull(options) {
var _super_func = null
if (typeof options == 'string') {
oldFullCalendar.apply(this, arguments)
} else {
options = options || {}
if (options.loading) {
_super_func = options.loading
}
options.loading = function (loading) {
console.log(loading, 'loading')
if (_super_func) {
_super_func.apply(this, arguments)
}
if (!loading) {
window.print()
}
}
oldFullCalendar.apply(this, arguments)
}
}
$.fn.fullCalendar = newFull
The first solution could probably be improved by overriding loading instead. Since it's the method that notify when loading has been processed and which is also apparently called after the eventAfterAllRender callback.
I can create infobubbles using the HERE maps Javascript API - eg from their documentation:
function addInfoBubble(map) {
map.set('center', new nokia.maps.geo.Coordinate(53.430, -2.961));
map.setZoomLevel(7);
var infoBubbles = new nokia.maps.map.component.InfoBubbles(),
TOUCH = nokia.maps.dom.Page.browser.touch,
CLICK = TOUCH ? 'tap' : 'click',
container = new nokia.maps.map.Container();
container.addListener(CLICK, function (evt) {
infoBubbles.openBubble(evt.target.html, evt.target.coordinate);
}, false);
map.components.add(infoBubbles);
map.objects.add(container);
addMarkerToContainer(container, [53.439, -2.221],
'<div><a href=\'http://www.mcfc.co.uk\' >Manchester City</a>' +
'</div><div >City of Manchester Stadium<br>Capacity: 48,000</div>');
addMarkerToContainer(container, [53.430, -2.961],
'<div ><a href=\'http://www.liverpoolfc.tv\' >Liverpool</a>' +
'</div><div >Anfield<br>Capacity: 45,362</div>');
}
function addMarkerToContainer(container, coordinate, html) {
var marker = new nokia.maps.map.StandardMarker(
coordinate,
{html: html}
);
container.objects.add(marker);
}
I would like to hook into the close event of the infobubble. I realise I could use jQuery to find the span that contains the close button (examining the markup, I believe it has the class nm_bubble_control_close) and do something when this is clicked. However I thought there would be a built-in event that I could use.
Does anyone know if there is a built-in event that fires when the infobubble is closed? I can't find anything in the documentation.
The InfoBubbles component has a property "openBubbleHandles". It's an OList you can observe to see when bubbles are opened (i.e. added to the list) or closed (removed from the list).
AFAIK there is no built-in event, there's also no related property which may be observed.
Possible workaround:
override the built-in close-method with a custom function:
container.addListener(CLICK, function (evt) {
var b = infoBubbles.openBubble(evt.target.html, evt.target.coordinate),
c = b.close;
b.close = function(){
//do what you want to
alert('bubble will be closed');
//execute the original close-method
c.apply(b)
}
}, false);
For the current HERE Javascript API version (3.x) the working solution can be found here:
HERE Maps - Infobubble close event / hook
I am trying to select events on fullcalendar, based on user selection.
Example: if user selects class A, then all classes with the same ID should turn green (using applied className).
I am having trouble applying classes to the other events that I can successfully select by ID. I guess my issue is combining the event objects with jQuery objects.
sample code:
eventClick: function(event) {
$(this).addClass("reg_selected"); //this works fine on selected event
var selectedID = event.id
alert(selectedID); //get event.ID, and use it to find similar ones.
var similarEvents = $("#calendar").fullCalendar('clientEvents',selectedID).addClass("reg_selected");
the error I get is:
addClass is not a function
I also tried this method of looping, and got the same error:
for (var i = 0; similarEvents.length > i ; i++){
alert(similarEvents[i].title);
similarEvents[i].className("reg_selected");
}
the alert() worked, but the className() generated the same error as above
This answer for a very similar situation, but when event classes are selected with round-trip to the event source for possible persistence in the db or checks.
Class name can be specified in the event object in the source as follows (start and end given for the context only):
[{
...
"className": "selected-event",
"start": '2017-05-01T08:30:00.0',
"ends": '2017-05-01T09:00:00.0',
...
}, ...]
The idea is that user clicks the event; ajax call to select events goes to backend; onsuccess, frontend javascript does$calendar.fullCalendar('rerenderEvents'); and receives the event source with events' classes. The immediate child of .fc-event-container gets the specified class, in the example above - selected-event.
As a result, the selection can be persisted on the backend.
clientEvents returns an array of matching objects. You need to iterate through the array (in your case similarEvents) and call addClass for each item
Update:
There is also issues using an id to update multiple events, using a filter function instead is a better way to go.
eventClick: function(event) {
var similarEvents = $("#calendar").fullCalendar('clientEvents', function(e) { return e.test === event.test });
for (var i = 0; similarEvents.length > i ; i++){
similarEvents[i].className = 'reg_selected';
$('#calendar').fullCalendar('updateEvent', similarEvents[i]);
}
},
See jsfiddle
For fullcalendar add event class, id and title see this.
if($('#eventTitle').val() == "Avilable") {
eventClass = "avilable";
}else {
eventClass = "unavilable";
}
$myCalendar.fullCalendar('renderEvent', {
id:response,
title: title.val(),
start: start.val(),
end: end.val(),
allDay: true,
className: eventClass,
color: color
}, true
);
I was able to get it working with the following code:
eventRender: function (eventObj, $el) {
$el.addClass(eventObj.ClassName);
},
eventObj.ClassName = "calendar-priority-warning"
I'm using the Vitrux theme in Wordpress that uses Isotope jQuery plugin to display a work porfolio. Isotope allows categories to be used to sort the items, but within the theme it's only possible to sort by one category at a time (e.g. 'Year' or 'Type', not 'Year' and 'Type'.
You can see a mock-up here: http://snaprockandpop.samcampsall.co.uk/shoots/
The jQuery attached to each category item, that sorts the posts, is as follows:
function (){
var selector = $(this).attr('data-filter');
$container_isotope.isotope({ filter: selector });
var $parent = $(this).parents(".filter_list");
$parent.find(".active").removeClass('active');
$(".filter_list").not($parent).find("li").removeClass('active').first().addClass("active");
$(this).parent().addClass("active");
return false;
}
I can see from the Isotope site that it's possible to use multiple filters, and I've found the authors notes on this here: http://jsfiddle.net/desandro/pJ6W8/31/
EDIT:
Editing the theme files has allowed me to assign appropriate classes and properties to the filter lists (you can see these in the page source) and I'm targeting them through an edited version of the jsfiddle to reflect the classes and id's in the theme styling:
$( function() {
var $container = $('#portfolio_container');
$container.isotope({
animationOptions: { duration: 300, easing: 'linear', queue: false },
getSortData : {
year : function ( $elem ) { return parseFloat( $elem.find('._year').text() ); },
live-shows : function ( $elem ) { return parseFloat( $elem.find('._live-shows').text() ); }
}
});
var filters = {};
$('.ql_filter a').click(function(){
var $this = $(this);
if ( $this.hasClass('selected') ) {
return;
}
var $optionSet = $this.parents('.filter_list');
$optionSet.find('.active').removeClass('active');
$this.addClass('active');
var group = $optionSet.attr('data-filter-group');
filters[ group ] = $this.attr('data-filter');
var isoFilters = [];
for ( var prop in filters ) {
isoFilters.push( filters[ prop ] )
}
var selector = isoFilters.join('');
$container.isotope({ filter: selector });
return false;
});
});
Two (fairly major) things:
1) I'm not 100% that I've edited this correctly. Despite Rich's excellent comments I'm still out of my depth. I'm particularly not clear on how to set-up the 'getSortData' section - I think it's right but any input would be great.
2) I'm not sure that this JavaScript is being initiated. At the moment I've placed it immediately before the closing head tag but a check on the page suggests that the original script outlined above is the one running on the filter items.
Any pointers at this stage would be fantastic!
I see what you mean. You are looking for the intersection of both filters and not the mutually exclusive filter values.
Short answer: Contact the theme vendor and see if they can make the intersection filters for you.
Longer assistance (not an answer):
Your ultimate goal is to get the Vitrux theme working the way you want.
Your first goal is to understand what the jsfiddle code is doing.
I can handle your first goal by explicating the code.
// hook into the JQuery Document Load event and run an anonymous function
$( function() {
// Create a variable called container
// make container refer to the element with ID Container
var $container = $('#container');
// initialize isotope
// Call the isotope method on the container element
$container.isotope({
// options...
//distracting options
animationOptions: { duration: 300, easing: 'linear', queue: false },
getSortData : {
price : function ( $elem ) { return parseFloat( $elem.find('.price').text() ); },
size : function ( $elem ) { return parseFloat( $elem.find('.size').text() ); }
}
});
// sorting button
//for the anchor tag that has a class of 'pricelow', wire up an anonymous function to the click event
$('a.pricelow').click(function(){
//Rerun the isotope method when it is clicked, pass an array of options as a parameter
$('#container').isotope({ sortBy : 'price',sortAscending : true });
//return false for the anonymous function. Not 100% sure why this is necessary but it has bitten me before
return false;
});
//removed the rest of the click methods, because it does the same thing with different params
//Here is what you are interested in understanding
//Create an empty filters object
var filters = {};
// filter buttons
//When an anchor tag with class filters is clicked, run our anonymous function
$('.filters a').click(function(){
//Create a variable that is the action anchor element
var $this = $(this);
// don't proceed if already selected by checking if a class of "selected" has already been applied to the anchor
if ( $this.hasClass('selected') ) {
return;
}
//Create an optionSet Variable, point it to the anchor's parent's class of "option-set"
var $optionSet = $this.parents('.option-set');
// change selected class
//Inside the optionSet, find elements that match the "selected" class and then remove the "selected class"
$optionSet.find('.selected').removeClass('selected');
// set this (the anchor element) class to "selected"
$this.addClass('selected');
// store filter value in object
// create a variable called 'group' that points to the optionsSet variable and grab the data-filter-group expando attribute
var group = $optionSet.attr('data-filter-group');
//Append to the filters object at the top of this section and set the data-filter-group value to the anchor tag's data-filter value
filters[ group ] = $this.attr('data-filter');
//create an isoFilters array variable
var isoFilters = [];
//Loop through each one of the items in filters (give the item an alias variable called 'prop'
for ( var prop in filters ) {
//push the prop into the isoFilters array (the opposite is pop)
isoFilters.push( filters[ prop ] )
//keep looping until there are no more items in the object
}
//create a variable called selector and turn the array into a string by joining all of the arrays together
var selector = isoFilters.join('');
//Like always, call the 'isotope' method of the 'container' element and pass our newly concatenated 'selector' string as the 'filter' option.
$container.isotope({ filter: selector });
//return false for some reason (maybe someone can expand on that)
return false;
});
});
Next is your ultimate goal which is modifying the Vitrux theme to handle intersection filters.
This gets a little tricky because
You have automatically generated tags from PHP to create the category links and the Year filter. So, there will be definitely some PHP code changes.
You must convert the jsfiddle code to handle your PHP changes
Try it using jQuery noconflict. In effect, replace any "$" with "jQuery" and see if it works.
Wordpress doesn't play well with the dollar sign.