I've been trying to get a FabricJs canvas work with multitouch pan and zoom, but to no avail. I've tried countless custom builds but the event doesn't have any touch information to work on. Here's the code I use:
let fabricCanvas = new fabric.Canvas('myCanvas', {
width: canvasContainer.current.offsetWidth,
height: canvasContainer.current.offsetHeight,
isDrawingMode: true
})
fabricCanvas.on({
'touch:gesture': function(e) {
console.log(e) // returns empty object wen fired with fabricCanvas.fire("touch:gesture")
}
});
fabricCanvas.fire("touch:gesture") // I can only make the listener fire, by doing this
How can I make the gestures provided work normally?
If you look at the library code, there's a line preventing gesture event being fired when isDrawingMode is true.
in /src/mixins/canvas_gestures.mixin.js
__onTransformGesture: function(e, self) {
if (this.isDrawingMode || !e.touches || e.touches.length !== 2 || 'gesture' !== self.gesture) {
return;
}
var target = this.findTarget(e);
if ('undefined' !== typeof target) {
this.__gesturesParams = {
e: e,
self: self,
target: target
};
this.__gesturesRenderer();
}
this.fire('touch:gesture', {
target: target, e: e, self: self
});
},
I'm also trying to use gesture with drawingMode and don't know why it prevents gesture on drawingMode.
Currently, I'm trying to use a custom build with modified source code.
Or you can try not to use isDrawingMode and use mouse events to implement freedraw
Related
I'm currently working on a react-native app and I'm trying to animate the layout of the login-screen when a keyboard is shown.
To track the state of the keyboard, I'm using this code:
componentDidMount() {
this.keyboardDidShowSub = Keyboard.addListener('keyboardDidShow', (event) => console.log(event));
this.keyboardDidHideSub = Keyboard.addListener('keyboardDidHide', (event) => console.log(event));
}
keyboardDidShow is working and returning:
Object {
"endCoordinates": Object {
"height": 286,
"screenX": 0,
"screenY": 354,
"width": 360,
},
}
However, keyboardDidHide is NOT working and returning null.
What could cause my problem? Thank you so much for your help!!
This is expected behaviour in Android. If you look at the underlying native code that is called when the keyboard is shown/hidden you can see what is sent back to the javascript side.
private void checkForKeyboardEvents() {
getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea);
final int heightDiff =
DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels - mVisibleViewArea.bottom;
if (mKeyboardHeight != heightDiff && heightDiff > mMinKeyboardHeightDetected) {
// keyboard is now showing, or the keyboard height has changed
mKeyboardHeight = heightDiff;
WritableMap params = Arguments.createMap();
WritableMap coordinates = Arguments.createMap();
coordinates.putDouble("screenY", PixelUtil.toDIPFromPixel(mVisibleViewArea.bottom));
coordinates.putDouble("screenX", PixelUtil.toDIPFromPixel(mVisibleViewArea.left));
coordinates.putDouble("width", PixelUtil.toDIPFromPixel(mVisibleViewArea.width()));
coordinates.putDouble("height", PixelUtil.toDIPFromPixel(mKeyboardHeight));
params.putMap("endCoordinates", coordinates);
sendEvent("keyboardDidShow", params);
} else if (mKeyboardHeight != 0 && heightDiff <= mMinKeyboardHeightDetected) {
// keyboard is now hidden
mKeyboardHeight = 0;
sendEvent("keyboardDidHide", null); // <- you can see here that when the keyboard is hidden it sends back null
}
}
It is worthwhile noting that in iOS that 'keyboardWillShow', 'keyboardDidShow', 'keyboardWillHide' and 'keyboardDidHide' will return an object.
I would like to create a canvas in which I would load images of some entity. the images of these entities could change from time to time.
FabricJs provides a built in serialization & de-serialization mechanism using following methods
fabric.Canvas#toJSON to serialize the canvas and
fabric.Canvas#loadFromJSON to de-serialize it but the problem with them is they serialize the image source as well which is enormous and useless in my case
(fabric.Canvas#toDatalessJSON & fabric.Canvas#loadFromDatalessJSON seems to work only for complex object but not images gitHub Ref)
whats is the approach to this?
should customize serialization by serializing all the required properties and recreate objects based on those info?
I have even try to create a subclass of Image removing src from toObject to avoid serializing it but it seems that does not work as well
here is what I have tried with subclass
fabric.MyImage = fabric.util.createClass(fabric.Image, {
type: 'MyImage',
initialize: function(options) {
options || (options = { })
this.callSuper('initialize', options)
this.set('artIndexes', options.artIndexes || '')
},
toObject: function() {
const TO = this.callSuper('toObject')
fabric.util.object.extend(TO, {
artIndexes: this.get('artIndexes')
})
delete TO.src
return TO
},
_render: function(ctx) {
this.callSuper('_render', ctx)
}// ,
// fromURL: function(a, b) {
// this.callSuper('fromURL', a, b)
// }
})
fabric.MyImage.fromObject = function (object, callback) {
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
delete object.objects
callback && callback(new fabric.MyImage(enlivenedObjects, object))
})
}
fabric.MyImage.async = true
and to add Image i would do somthing like this
let img = new fabric.MyImage({entetyReference: entetyRef})
img.setSrc(`${imageSource}`, image => {
this.canvas.add(img)
})
but during serialization and de-serializatiin using FabricJs provided method the object get lost from canvas
I am using latest version of fabricJs
I am building a bot with the botbuilder framework using node, and I am now trying to use the CardAction.dialogAction:
builder.CardAction.dialogAction(session, 'help', 'topic:mytopic', 'Click me')
It generates a message that looks like this:
action?help=topic:mytopic
Now I need to route that action to the correct dialog to handle it, but I can't figure out where and how to do that. Seeing as this is a builtin feature, I figured there should be easy ways of doing this already?
Appreciate the help.
For a lack of any other options at this point I resorted to writing my own simple action recognizer. It is no beauty, but it does the trick.
function action_recognizer() {
return {
recognize: function (context, done) {
let intent = { score: 0.0 }
const text = context.message.text
if (text) {
const m = text.match(/action\?(\w+)=?(.+)/)
logger.debug(m)
if (m) {
switch (m[1]) {
case 'help':
intent = { score: 1.0, intent: 'help' }
if (m.length > 2) {
intent.entities = [{entity: m[2], type: 'custom_intent'}]
}
break
}
}
}
done(null, intent)
}
}
}
This will basically direct to my help:/ dialog. Which in turn will read the entities list (if custom_intent types are available).
I am building an Electron based application that contains a grid containing unique rows. I would like a context-menu that is specific to each row. Here is an example:
Although this screen shot is cropped, you can see there are multiple rows and each row contains separate data. Since I'd like to right-click on a row and get a unique context menu, I have implemented electron-context-menu, which does work on the first right click, but then subsequent right-clicks causes a stacking effect of context menus.
Specifically, here is what happens:
I right click on Row-1 and the proper context menu shows up
I right click on Row-2 and a repeat of the context menu for Row-1 shows up then Row-2's context menu shows up. (Notice in the screen shot the context menu showing does not correspond to the row my mouse is over)
This repeats itself.
In React.JS, here is my listener, which collects the contextmenu object as needed by the electron-context-menu module:
handleContextMenu() {
this.props.contextMenu({
window: electron.remote.BrowserWindow.getFocusedWindow(),
prepend: (params, browserWindow) => [{
label: `Library Compare ${this.state.msn}`,
click: () => this.runLibCompare()
}],
append: (params, browserWindow) => [{
label: '---',
}]
})
};
Where this.props.contextMenu(...) perculates up the React.JS components to be fed into:
const contextMenu = eRequire('electron-context-menu');
I have done some massive debugging and I don't think the issue is the module. The module I am using essentially organizes the information about the context menu and then uses electron.remote functions and a menu.popup function which comes from electron internals. Here is a link to the specific line in github.
const menu = (electron.Menu || electron.remote.Menu).buildFromTemplate(menuTpl);
menu.popup(electron.remote ? electron.remote.getCurrentWindow() : win);
This call to menu.popup leads to this line in electron.
const remoteMemberFunction = function (...args) {
if (this && this.constructor === remoteMemberFunction) {
// Constructor call.
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
return metaToValue(ret)
} else {
// Call member function.
let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
return metaToValue(ret)
}
}
So I see a call to ipcRender.sendSync -- however when I add debugging statements in ipcMain's receiver of those calls, I don't see any output!
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
try {
args = unwrapArgs(event.sender, args)
let obj = objectsRegistry.get(id)
if (obj == null) {
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
}
callFunction(event, obj[method], obj, args)
} catch (error) {
event.returnValue = exceptionToMeta(error)
}
})
When I added debug statements to the above function, I didn't see any output. And that is where my search his a wall.
I am using electron 1.4.15. I know this issue should be resolvable, after-all the Atom IDE (which is electron based) does not have this issue even though it has multiple context menus.
I think there is some memory I need to clear somewhere, I just can't figure out how to clear the stack of previous context menus!
I solve this by first getting the target of the click using e.target. Then, depending on that, I call the corresponding contextmenu. If target hit is not in the list of targets for my app, I use a default contextmenu.
window.addEventListener(
"contextmenu",
e => {
e.preventDefault();
if (e.target.id === 'fullscr'){
console.log(e && e.target);
// e.preventDefault();
mymenu.popup(remote.getCurrentWindow());
}else{
editmenu.popup(remote.getCurrentWindow());
}
console.log(e.which);
},
false
);
All,
I am trying to have the crosshair track the three waveforms.
The crosshair does show up, and the tracking does not (oops ... see EDIT) take place. Please note - as specified in the picture - that the legend for the horizontal bar is simply a DIV styled to look like a legend from FLOT.
The CodePen can be found here
As the program is executed, the 'legend' becomes null, and the strings do not update ... the code is:
legends = $(".legendLabel");
legends.each(function () {
// fix the widths so they don't jump around
$(this).css('width', $(this).width());
});
I ended up having to put this code in the updateLegend() function. The question is 'why'? Am I sitting on a time bomb? :-) :-)
On a side note, thank you again to Raidri for getting me started earlier in the day.
-- EDIT -- The tracking DOES take place (I had managed to fix it in the middle of posting the question). The only doubt I have is about having to re-initialize variable 'legend' in function updateLegend()
-- EDIT 2 -- The code works in Chrome and Firefox, but fails in IE: 'Object doesn't support property or method "assign"'. I found the solution here
The code below will fix the problem ...
if (typeof Object.assign != 'function') {
Object.assign = function(target) {
'use strict';
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
target = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source != null) {
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
}
return target;
};
}
$.each(data1, function (idx, item) {
Object.assign(item, {
stack: true,
lines: {show: false, steps: false },
bars: {show: true, horizontal:true, width: 1}
});
});