I have some difficulties and would like your help.
I want to apply a FreeTransform in some objects with a particular class or id.
For example ... Within my HTML, I want to put an image gallery. And I would like the script freetransform apply to all images that have a specific class.
I have an example of what I did so far.
var r = Raphael(document.getElementById('workspace'));
var img = r.image('http://www.porcopedia.com/images/thumb/Simbolo_Goi%C3%A1s.jpg/600px-Simbolo_Goi%C3%A1s.jpg', 10, 10, 200, 200);
register(img);
function register(el) {
el.data('dragged', false);
el.click(function() {
// Toggle handles on click if no drag occurred
if ( !this.data('dragged') ) {
if ( this.freeTransform.handles.center === null ) {
this.toFront();
this.freeTransform.showHandles();
} else {
this.freeTransform.hideHandles();
}
}
});
r.freeTransform(el, {attrs: {
fill: 'black',
stroke: '#111'
}, drag: true,
keepRatio: ['axisX', 'axisY','bboxCorners'],
size: 6,
scale: [ 'axisX', 'axisY', 'bboxCorners', 'bboxSides' ],
rotate: true,
draw: ['bbox', 'circle']}, function(ft, events) {
if ( events.indexOf('drag start') !== -1 ) {
el.data('dragged', false);
}
if ( events.indexOf('drag') !== -1 ) {
el.data('dragged', true);
}
}).hideHandles();
}
Example JSFIDDLE
Related
I am working on fullcalendar events and need to show a confirmation box based on some condition once on UI, when events overlap.
The action performed in this popup will decided whether to overlap an event or not.
Issue is, eventOverlap callback is called several times, even when the dragged event is hover over the other event, generating numerous calls.
Is there any way to get both static and moved event only once?
this.calenderOptions = {
contentHeight: 300,
plugins: [resourceTimelinePlugin, interaction],
editable: true,
header: {
left: "prev,next",
center: "title",
right: "prev"
},
resources: this.resources,
defaultView: "resourceTimelineDay",
timeZone: "local",
droppable: true,
eventReceive: info => {
const ref = this.dialogService.open(ConfirmationPopup, {
header: "Confirm Action",
width: "20%",
contentStyle: { "max-height": "600px", overflow: "auto" }
});
ref.onClose.subscribe(data => {
if (data === "no") {
info.event.remove();
} else if (data === "yes") {
this.row_id = info.draggedEl.getAttribute("row-id");
return true;
} else {
info.event.remove();
return false;
}
});
},
eventOverlap: (stillEvent, movingEvent) => {
// need to open a popover here only once.
console.log("eventOverlap");
return true
},
eventDrop : info => {
if(info['newResource'] && info['oldResource']){
if(info['newResource']['id'] != info['oldResource']['id']){
info.revert();
}
}
console.log("eventDrop")
},
eventClick : info => {
console.log(info);
},
drop:info => {
console.log("drop");
},
slotLabelFormat: {
hour: "2-digit",
minute: "2-digit",
meridiem: false,
hour12: false
}
};
new ThirdPartyDraggable(gridEL, {
itemSelector: ".ag-row",
eventData: eventEl => {
let rowId = eventEl.getAttribute("row-id");
let data = this.commonService.grid_service.getRowNode(rowId);
let color = null;
if(data.data["status"] == 1){
color = "#DEC181";
}
else if (data.data["status"] == 2){
color = "#A56124";
}
else {
color = "#000000";
}
return {
title: data.data["shipmentId"],
duration: data.data["est"],
color : color,
className: "test",
extendedProps: [
{
est: data.data["est"],
shipmentId: data.data["shipmentId"]
}
]
};
}
});
this.calendar = new Calendar(
this.calenderComponent.element.nativeElement,
this.calenderOptions
);
this.calendar.render();
I had a similar issue with one project at work.
In my case I have to check if an event (insert by the user with a datepicker component) overlap in the calendar or not.
So, my idea is to bypass the eventOverlap handler and use the eventDrop handler for call a function that check if the new event overlap or not others.
The function that I used in my case is the one below:
// dateStart and dateEnd have to be date strings in ISO format (ex. 1980-10-10)
function isOverlapping(dateStart, dateEnd) {
var calendarEvents = $('#calendar').fullCalendar('clientEvents');
dateStart.setHours(0, 0, 0, 0);
dateEnd.setHours(0, 0, 0, 0);
var eventStart;
var eventEnd;
for (i in calendarEvents) {
if (calendarEvents[i].id != 1 && calendarEvents[i].type == undefined) {
eventStart = new Date(calendarEvents[i].start._d);
eventStart.setHours(0, 0, 0, 0);
eventEnd = new Date(calendarEvents[i].end._d);
eventEnd.setHours(0, 0, 0, 0);
if (eventStart.getTime() < dateEnd.getTime() && eventEnd.getTime() > dateStart.getTime()) {
return true;
}
}
}
return false;
}
With the result of this function you can check if your event overlap others or not, so you can show your confirmation box only if the event overlap and only one time.
Sorry for my bad english, I hope my answer can be useful!
I am able to show marker/icons using google map data layer and geoJson file(s). But instead of icons, is it possible to show a text (at particular location on map)?
It should not be part of InfoWindow. I am looking for something like Sample
I have tried using 'title' but it's just a label shown on mouse over of icon. Please help.
gmap.addListener('zoom_changed', function () {
var gmapZoom = gmap.GetZoom();
if (gmapZoom >= 0 && gmapZoom <= 21)
{
if (bJSONLoaded === false)
{
var setDataStyle = function (feature) {
var status = feature.getProperty('status');
switch (status) {
case "Active":
return {
icon: 'http://maps.google.com/mapfiles/ms/icons/green-dot.png',
title: 'test 1'
};
case "Pending":
return {
icon: 'http://maps.google.com/mapfiles/ms/icons/orange-dot.png'
};
}
};
pendingJSON.loadGeoJson('http://url/GoogleTestPending.json');
pendingJSON.setStyle(setDataStyle);
pendingJSON.setMap(gmap);
activeJSON.loadGeoJson('http://url/GoogleTestActive.json');
activeJSON.setStyle(setDataStyle);
activeJSON.setMap(gmap);
bJSONLoaded = true;
}
else
{
if (!testJSON.getMap()) {
console.log('setting map');
pendingJSON.setMap(gmap);
activeJSON.setMap(gmap);
testJSON.setMap(gmap);
}
}
}
else
{
if (bJSONLoaded === true && testJSON.getMap() != null)
{
//remove pins
pendingJSON.setMap(null);
activeJSON.setMap(null);
testJSON.setMap(null);
}
}
});
Solved it by using OverlayView.
customEntity.prototype = new google.maps.OverlayView();
And then implemented required functions like draw, onAdd, onRemove, etc.
on my Site www.yourpod.de the Navigation Bar turns into Black when scrolling. I just want to show the Black one which is fixed on the top. Can anybody held me?
Replace the following code in custom.js
var transparent = true;
if ($( '.navbar-color-on-scroll' ).length !== 0) {
var navbarHome = $( '.navbar-color-on-scroll' ),
headerWithTopbar = 0;
if ( navbarHome.hasClass( 'header-with-topbar' ) ) {
headerWithTopbar = 40;
}
$( window ).on(
'scroll', debounce(
function () {
if ($( document ).scrollTop() > headerWithTopbar) {
if (transparent) {
transparent = false;
navbarHome.removeClass( 'navbar-transparent' );
navbarHome.addClass( 'navbar-not-transparent' );
}
} else {
if ( ! transparent) {
transparent = true;
navbarHome.addClass( 'navbar-transparent' );
navbarHome.removeClass( 'navbar-not-transparent' );
}
}
}, 17
)
);
}
with this line
$( '.navbar-color-on-scroll' ).addClass('navbar-scroll-point navbar-not-transparent');
the recent v0.3.0 blog post mentions WebVR 1.0 support allowing "us to have different content on the desktop display than the headset, opening the door for asynchronous gameplay and spectator modes." This is precisely what I'm trying to get working. I'm looking to have one camera in the scene represent the viewpoint of the HMD and a secondary camera represent a spectator of the same scene and render that view to a canvas on the same webpage. 0.3.0 removes the ability to render a-scene to a specific canvas in favor of embedded component. Any thoughts on how to accomplish two cameras rendering a single scene simultaneously?
My intention is to have a the desktop display show what a user is doing from a different perspective. My end goal is to be able to build a mixed reality green screen component.
While there may be a better or cleaner way to do this in the future, I was able to get a second camera rendering by looking at examples of how this is done in the THREE.js world.
I add a component to a non-active camera called spectator. in the init function I set up a new renderer and attach to div outside the scene to create a new canvas. I then call the render method inside the tick() part of the lifecycle.
I have not worked out how to isolate the movement of this camera yet. The default look controls of the 0.3.0 aframe scene still control both camera
Source code:
https://gist.github.com/derickson/334a48eb1f53f6891c59a2c137c180fa
I've created a set of components that can help with this. https://github.com/diarmidmackenzie/aframe-multi-camera
Here's an example showing usage with A-Frame 1.2.0 to display the main camera on the left half of the screen, and a secondary camera on the right half.
<!DOCTYPE html>
<html>
<head>
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/diarmidmackenzie/aframe-multi-camera#latest/src/multi-camera.min.js"></script>
</head>
<body>
<div>
<a-scene>
<a-entity camera look-controls wasd-controls position="0 1.6 0">
<!-- first secondary camera is a child of the main camera, so that it always has the same position / rotation -->
<!-- replace main camera (since main camera is rendered across the whole screen, which we don't want) -->
<a-entity
id="camera1"
secondary-camera="outputElement:#viewport1;sequence: replace"
>
</a-entity>
</a-entity>
<!-- PUT YOUR SCENE CONTENT HERE-->
<!-- position of 2nd secondary camera-->
<a-entity
id="camera2"
secondary-camera="outputElement:#viewport2"
position="8 1.6 -6"
rotation="0 90 0"
>
</a-entity>
</a-scene>
</div>
<!-- standard HTML to contrl layout of the two viewports-->
<div style="width: 100%; height:100%; display: flex">
<div id="viewport1" style="width: 50%; height:100%"></div>
<div id="viewport2" style="width: 50%; height:100%"></div>
</div>
</body>
</html>
Also here as a glitch: https://glitch.com/edit/#!/recondite-polar-hyssop
It's also been suggested that I post the entire source code for the multi-camera component here.
Here it is...
/* System that supports capture of the the main A-Frame render() call
by add-render-call */
AFRAME.registerSystem('add-render-call', {
init() {
this.render = this.render.bind(this);
this.originalRender = this.el.sceneEl.renderer.render;
this.el.sceneEl.renderer.render = this.render;
this.el.sceneEl.renderer.autoClear = false;
this.preRenderCalls = [];
this.postRenderCalls = [];
this.suppresssDefaultRenderCount = 0;
},
addPreRenderCall(render) {
this.preRenderCalls.push(render)
},
removePreRenderCall(render) {
const index = this.preRenderCalls.indexOf(render);
if (index > -1) {
this.preRenderCalls.splice(index, 1);
}
},
addPostRenderCall(render) {
this.postRenderCalls.push(render)
},
removePostRenderCall(render) {
const index = this.postRenderCalls.indexOf(render);
if (index > -1) {
this.postRenderCalls.splice(index, 1);
}
else {
console.warn("Unexpected failure to remove render call")
}
},
suppressOriginalRender() {
this.suppresssDefaultRenderCount++;
},
unsuppressOriginalRender() {
this.suppresssDefaultRenderCount--;
if (this.suppresssDefaultRenderCount < 0) {
console.warn("Unexpected unsuppression of original render")
this.suppresssDefaultRenderCount = 0;
}
},
render(scene, camera) {
renderer = this.el.sceneEl.renderer
// set up THREE.js stats to correctly count across all render calls.
renderer.info.autoReset = false;
renderer.info.reset();
this.preRenderCalls.forEach((f) => f());
if (this.suppresssDefaultRenderCount <= 0) {
this.originalRender.call(renderer, scene, camera)
}
this.postRenderCalls.forEach((f) => f());
}
});
/* Component that captures the main A-Frame render() call
and adds an additional render call.
Must specify an entity and component that expose a function call render(). */
AFRAME.registerComponent('add-render-call', {
multiple: true,
schema: {
entity: {type: 'selector'},
componentName: {type: 'string'},
sequence: {type: 'string', oneOf: ['before', 'after', 'replace'], default: 'after'}
},
init() {
this.invokeRender = this.invokeRender.bind(this);
},
update(oldData) {
// first clean up any old settings.
this.removeSettings(oldData)
// now add new settings.
if (this.data.sequence === "before") {
this.system.addPreRenderCall(this.invokeRender)
}
if (this.data.sequence === "replace") {
this.system.suppressOriginalRender()
}
if (this.data.sequence === "after" ||
this.data.sequence === "replace")
{
this.system.addPostRenderCall(this.invokeRender)
}
},
remove() {
this.removeSettings(this.data)
},
removeSettings(data) {
if (data.sequence === "before") {
this.system.removePreRenderCall(this.invokeRender)
}
if (data.sequence === "replace") {
this.system.unsuppressOriginalRender()
}
if (data.sequence === "after" ||
data.sequence === "replace")
{
this.system.removePostRenderCall(this.invokeRender)
}
},
invokeRender()
{
const componentName = this.data.componentName;
if ((this.data.entity) &&
(this.data.entity.components[componentName])) {
this.data.entity.components[componentName].render(this.el.sceneEl.renderer, this.system.originalRender);
}
}
});
/* Component to set layers via HTML attribute. */
AFRAME.registerComponent('layers', {
schema : {type: 'number', default: 0},
init: function() {
setObjectLayer = function(object, layer) {
if (!object.el ||
!object.el.hasAttribute('keep-default-layer')) {
object.layers.set(layer);
}
object.children.forEach(o => setObjectLayer(o, layer));
}
this.el.addEventListener("loaded", () => {
setObjectLayer(this.el.object3D, this.data);
});
if (this.el.hasAttribute('text')) {
this.el.addEventListener("textfontset", () => {
setObjectLayer(this.el.object3D, this.data);
});
}
}
});
/* This component has code in common with viewpoint-selector-renderer
However it's a completely generic stripped-down version, which
just delivers the 2nd camera function.
i.e. it is missing:
- The positioning of the viewpoint-selector entity.
- The cursor / raycaster elements.
*/
AFRAME.registerComponent('secondary-camera', {
schema: {
output: {type: 'string', oneOf: ['screen', 'plane'], default: 'screen'},
outputElement: {type: 'selector'},
cameraType: {type: 'string', oneOf: ['perspective, orthographic'], default: 'perspective'},
sequence: {type: 'string', oneOf: ['before', 'after', 'replace'], default: 'after'},
quality: {type: 'string', oneOf: ['high, low'], default: 'high'}
},
init() {
if (!this.el.id) {
console.error("No id specified on entity. secondary-camera only works on entities with an id")
}
this.savedViewport = new THREE.Vector4();
this.sceneInfo = this.prepareScene();
this.activeRenderTarget = 0;
// add the render call to the scene
this.el.sceneEl.setAttribute(`add-render-call__${this.el.id}`,
{entity: `#${this.el.id}`,
componentName: "secondary-camera",
sequence: this.data.sequence});
// if there is a cursor on this entity, set it up to read this camera.
if (this.el.hasAttribute('cursor')) {
this.el.setAttribute("cursor", "canvas: user; camera: user");
this.el.addEventListener('loaded', () => {
this.el.components['raycaster'].raycaster.layers.mask = this.el.object3D.layers.mask;
const cursor = this.el.components['cursor'];
cursor.removeEventListeners();
cursor.camera = this.camera;
cursor.canvas = this.data.outputElement;
cursor.canvasBounds = cursor.canvas.getBoundingClientRect();
cursor.addEventListeners();
cursor.updateMouseEventListeners();
});
}
if (this.data.output === 'plane') {
if (!this.data.outputElement.hasLoaded) {
this.data.outputElement.addEventListener("loaded", () => {
this.configureCameraToPlane()
});
} else {
this.configureCameraToPlane()
}
}
},
configureCameraToPlane() {
const object = this.data.outputElement.getObject3D('mesh');
function nearestPowerOf2(n) {
return 1 << 31 - Math.clz32(n);
}
// 2 * nearest power of 2 gives a nice look, but at a perf cost.
const factor = (this.data.quality === 'high') ? 2 : 1;
const width = factor * nearestPowerOf2(window.innerWidth * window.devicePixelRatio);
const height = factor * nearestPowerOf2(window.innerHeight * window.devicePixelRatio);
function newRenderTarget() {
const target = new THREE.WebGLRenderTarget(width,
height,
{
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
stencilBuffer: false,
generateMipmaps: false
});
return target;
}
// We use 2 render targets, and alternate each frame, so that we are
// never rendering to a target that is actually in front of the camera.
this.renderTargets = [newRenderTarget(),
newRenderTarget()]
this.camera.aspect = object.geometry.parameters.width /
object.geometry.parameters.height;
},
remove() {
this.el.sceneEl.removeAttribute(`add-render-call__${this.el.id}`);
if (this.renderTargets) {
this.renderTargets[0].dispose();
this.renderTargets[1].dispose();
}
// "Remove" code does not tidy up adjustments made to cursor component.
// rarely necessary as cursor is typically put in place at the same time
// as the secondary camera, and so will be disposed of at the same time.
},
prepareScene() {
this.scene = this.el.sceneEl.object3D;
const width = 2;
const height = 2;
if (this.data.cameraType === "orthographic") {
this.camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 );
}
else {
this.camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000);
}
this.scene.add(this.camera);
return;
},
render(renderer, renderFunction) {
// don't bother rendering to screen in VR mode.
if (this.data.output === "screen" && this.el.sceneEl.is('vr-mode')) return;
var elemRect;
if (this.data.output === "screen") {
const elem = this.data.outputElement;
// get the viewport relative position of this element
elemRect = elem.getBoundingClientRect();
this.camera.aspect = elemRect.width / elemRect.height;
}
// Camera position & layers match this entity.
this.el.object3D.getWorldPosition(this.camera.position);
this.el.object3D.getWorldQuaternion(this.camera.quaternion);
this.camera.layers.mask = this.el.object3D.layers.mask;
this.camera.updateProjectionMatrix();
if (this.data.output === "screen") {
// "bottom" position is relative to the whole viewport, not just the canvas.
// We need to turn this into a distance from the bottom of the canvas.
// We need to consider the header bar above the canvas, and the size of the canvas.
const mainRect = renderer.domElement.getBoundingClientRect();
renderer.getViewport(this.savedViewport);
renderer.setViewport(elemRect.left - mainRect.left,
mainRect.bottom - elemRect.bottom,
elemRect.width,
elemRect.height);
renderFunction.call(renderer, this.scene, this.camera);
renderer.setViewport(this.savedViewport);
}
else {
// target === "plane"
// store off current renderer properties so that they can be restored.
const currentRenderTarget = renderer.getRenderTarget();
const currentXrEnabled = renderer.xr.enabled;
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
// temporarily override renderer proeperties for rendering to a texture.
renderer.xr.enabled = false; // Avoid camera modification
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
const renderTarget = this.renderTargets[this.activeRenderTarget];
renderTarget.texture.encoding = renderer.outputEncoding;
renderer.setRenderTarget(renderTarget);
renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
renderer.clear();
renderFunction.call(renderer, this.scene, this.camera);
this.data.outputElement.getObject3D('mesh').material.map = renderTarget.texture;
// restore original renderer settings.
renderer.setRenderTarget(currentRenderTarget);
renderer.xr.enabled = currentXrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
this.activeRenderTarget = 1 - this.activeRenderTarget;
}
}
});
clip-path:shape() does not seem to work in IE (no surprise) and Firefox (a bit surprised). Is there a way to test for clip-path support? I use modernizr. (By the way, I know I can get this to work using SVGs and -webkit-clip-path:url(#mySVG))
You asked this a while ago, and to be honest, I'm not sure if Modernizr has yet to add support for this, but it's pretty easy to roll your own test in this case.
The steps are:
Create, but do not append, a DOM element.
Check that it supports any kind of clipPath by checking the JS style attribute of the newly created element (element.style.clipPath === '' if it can support it).
Check that it supports CSS clip path shapes by making element.style.clipPath equal some valid CSS clip path shape.
Of course, it's a little more complex than that, as you have to check for vendor-specific prefixes.
Here it is all together:
var areClipPathShapesSupported = function () {
var base = 'clipPath',
prefixes = [ 'webkit', 'moz', 'ms', 'o' ],
properties = [ base ],
testElement = document.createElement( 'testelement' ),
attribute = 'polygon(50% 0%, 0% 100%, 100% 100%)';
// Push the prefixed properties into the array of properties.
for ( var i = 0, l = prefixes.length; i < l; i++ ) {
var prefixedProperty = prefixes[i] + base.charAt( 0 ).toUpperCase() + base.slice( 1 ); // remember to capitalize!
properties.push( prefixedProperty );
}
// Interate over the properties and see if they pass two tests.
for ( var i = 0, l = properties.length; i < l; i++ ) {
var property = properties[i];
// First, they need to even support clip-path (IE <= 11 does not)...
if ( testElement.style[property] === '' ) {
// Second, we need to see what happens when we try to create a CSS shape...
testElement.style[property] = attribute;
if ( testElement.style[property] !== '' ) {
return true;
}
}
}
return false;
};
Here's a codepen proof-of-concept: http://codepen.io/anon/pen/YXyyMJ
You can test with Modernizr.
(function (Modernizr) {
// Here are all the values we will test. If you want to use just one or two, comment out the lines of test you don't need.
var tests = [{
name: 'svg',
value: 'url(#test)'
}, // False positive in IE, supports SVG clip-path, but not on HTML element
{
name: 'inset',
value: 'inset(10px 20px 30px 40px)'
}, {
name: 'circle',
value: 'circle(60px at center)'
}, {
name: 'ellipse',
value: 'ellipse(50% 50% at 50% 50%)'
}, {
name: 'polygon',
value: 'polygon(50% 0%, 0% 100%, 100% 100%)'
}
];
var t = 0, name, value, prop;
for (; t < tests.length; t++) {
name = tests[t].name;
value = tests[t].value;
Modernizr.addTest('cssclippath' + name, function () {
// Try using window.CSS.supports
if ('CSS' in window && 'supports' in window.CSS) {
for (var i = 0; i < Modernizr._prefixes.length; i++) {
prop = Modernizr._prefixes[i] + 'clip-path'
if (window.CSS.supports(prop, value)) {
return true;
}
}
return false;
}
// Otherwise, use Modernizr.testStyles and examine the property manually
return Modernizr.testStyles('#modernizr { ' + Modernizr._prefixes.join('clip-path:' + value + '; ') + ' }', function (elem, rule) {
var style = getComputedStyle(elem),
clip = style.clipPath;
if (!clip || clip == "none") {
clip = false;
for (var i = 0; i < Modernizr._domPrefixes.length; i++) {
test = Modernizr._domPrefixes[i] + 'ClipPath';
if (style[test] && style[test] !== "none") {
clip = true;
break;
}
}
}
return Modernizr.testProp('clipPath') && clip;
});
});
}
})(Modernizr);
Check this codepen to see it in action.