I have a site made with three.js and ascii effect renderer (constantly moving image, which can be controlled with mouse interaction), and want to save the generated image of whole generated body with html2canvas, but it doesn't work, this is the code and the error message
const animationCanvas = document.body;
const saveButton = document.getElementById("savelink");
let dataURL = null
saveButton.addEventListener("click", generateImage);
// Generate image
async function generateImage() {
await window.html2canvas(animationCanvas).then(canvas => {
dataURL = canvas.toDataURL();
}).then( () => {
downloadImage(dataURL, 'picture.jpeg');
} ).then(() => location.reload() );
}
// Download image
function downloadImage(data, filename = 'untitled.jpeg') {
var a = document.createElement('a');
a.href = data;
a.download = filename;
document.body.appendChild(a);
a.click();
}
If I try to save some individual div of the page, it works without problems. Any idea what can be wrong?
EDIT: as suggested by #2pha, this is the error when using unminified html2canvas
length-percentage.ts:38 Uncaught (in promise) TypeError: Cannot read property 'type' of undefined
at getAbsoluteValue (length-percentage.ts:38)
at getAbsoluteValueForTuple (length-percentage.ts:35)
at calculateBackgroundRendering (background.ts:63)
at canvas-renderer.ts:556
at step (tslib.es6.js:97)
at Object.next (tslib.es6.js:78)
at step (tslib.es6.js:82)
at Object.next (tslib.es6.js:78)
at fulfilled (tslib.es6.js:68)
getAbsoluteValue # length-percentage.ts:38
getAbsoluteValueForTuple # length-percentage.ts:35
calculateBackgroundRendering # background.ts:63
(anonymous) # canvas-renderer.ts:556
step # tslib.es6.js:97
(anonymous) # tslib.es6.js:78
step # tslib.es6.js:82
(anonymous) # tslib.es6.js:78
fulfilled # tslib.es6.js:68
async function (async)
generateImage # index.html:432
After many hours of checks I found out that a select/option element was making this error, so I had to remove it before the html2canvas generating
<select id="choose">
<option value="choice1" selected="selected">choice1</option>
<option value="choice2">choice2</option>
<option value="choice3">choice3</option>
<option value="choice4">choice4</option>
</select>
With this style:
select {
...
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='black'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat;
background-size: 12px;
...
}
The svg made the mess. So rule-of-thumb: no svg for html2canvas generation...
Related
I am trying to create Figma Text styles via a plugin that reads from my design tokens. The problem I am having is when setting the custom font, the promise to load it does not resolve, so it seems to stall the function.
Am I missing something here?
async function createNewTextStyle(token) {
// Style does not yet exist, so create it
const newStyle = figma.createTextStyle();
// the new text style is preloaded with Roboto, so we need to load that
await figma.loadFontAsync(newStyle.fontName);
// This is the font i want to set my text style to.
// it seems that this promise never resolves (or errors).
await figma.loadFontAsync({
family: 'SF Pro Text',
style: 'Regular',
});
// Set the properties of the text style
newStyle.fontName = {
family: 'SF Pro Text',
style: 'Regular',
};
newStyle.name = designToken.name;
newStyle.fontSize = designToken.value;
newStyle.lineHeight = { value: designToken.lineHeight, unit: 'PIXELS' };
}
Hmmm... it looks like you're missing the .then callback that's usually used to handle a promise resolution.
Also, your variable name designToken is not defined in your code, but you're using token (from your function parameters).
Try this in your plugin:
async function createNewTextStyle(token) {
// Style does not yet exist, so create it
const newStyle = figma.createTextStyle();
// the new text style is preloaded with Roboto, so we need to load that
await figma.loadFontAsync(newStyle.fontName);
// This is the font i want to set my text style to.
figma.loadFontAsync({
family: 'SF Pro Text',
style: 'Regular',
}).then(() => {
// Set the properties of the text style
newStyle.fontName = {
family: 'SF Pro Text',
style: 'Regular',
};
newStyle.name = token.name;
newStyle.fontSize = token.value;
newStyle.lineHeight = { value: token.lineHeight, unit: 'PIXELS' };
});
}
Currently, I'm using the React version of WebChat on my site. (https://github.com/Microsoft/BotFramework-WebChat)
I'm building a feature to hide and show the WebChat element on a page. When the page first loads, the WebChat shows. When clicking on top of the WebChat (.chatbot-top-pane), the WebChat minimizes (.chat-window hides). When clicking on the div again to maximize the window (show .chat-window), the WebChat shows then tries to reconnect to the server, but fails. Why does it try to reconnect? Did the session somehow end in the background and it's trying to resume using the same token?
I tried using display: none, visibility: hidden on .chat-window.
<div className="chatbot-container">
<div className="chatbot-top-pane" onClick={handleToggleStatus.bind(this)}>
<p className="chatbot-pane-label-text">Chatbot Disco</p>
</div>
<ReactWebChat className="chat-window" directLine={createDirectLine({ token })} />
</div>
Also, I tried wrapping the .chat-window with a div and tried hiding .chat-window-container, instead:
<div className="chatbot-container">
<div className="chatbot-top-pane" onClick={handleToggleStatus.bind(this)}>
<p className="chatbot-pane-label-text">Chatbot Disco</p>
</div>
<div className="chat-window-container">
<ReactWebChat className="chat-window" directLine={createDirectLine({ token })} />
</div>
</div>
Below is a screenshot of the error message displayed on the WebChat
Error message on WebChat
I tried the same thing on the iframe version of the WebChat and had no issue at all.
When Web Chat dismounts, it triggers the DIRECT_LINE/DISCONNECT action to fire which causes DirectLine to disconnect. However, looking over your code, I'm not sure why you would have an issue with that especially since your implementation looks similar to the Minimizable WebChat Sample and you are not dismounting the Web Chat component.
I put together a simplified version of the sample that seems to work fine.
Web Chat v4
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import ReactWebChat, { createDirectLine, createStore } from 'botframework-webchat';
import classNames from 'classnames';
const WebChat = () => {
const [token, setToken] = useState('');
const [minimized, setMinimized] = useState(false);
const directLine = useMemo(() => createDirectLine({ token }), [token]);
const store = useMemo(() => createStore(), []);
const toggleChatVisivility = useCallback(() => setMinimized(minimized => !minimized), [setMinimized]);
useEffect(() => {
(async function () {
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
setToken(token);
})();
}, []);
return (
<React.Fragment>
<button onClick={toggleChatVisivility}>{minimized ? 'Show Chat' : 'Hide Chat'}</button>
<ReactWebChat className={classNames('webchat__chat-container', minimized ? 'webchat__hidden': 'webchat__visivle')} directLine={directLine} store={store} />
</React.Fragment>);
}
export default WebChat;
css
.webchat__chat-container {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.webchat__hidden {
visibility: hidden;
}
Screen capture
Hopefully the code snippets above help you figure out what's missing from your implementation. Sorry, I couldn't be more helpful. Let me know if you run into anymore questions. I'm happy to help answer them.
I am new to VueJS + Laravel after years using CodeIgniter and trying to understand how things work with Laravel + VueJS. Anyway, I got a test page running with a Vue Loading Layer package that triggers a loading overlay on the onClick event that loads a new page using router.push. I read the documentation that there is 2 additional parameters, ie. onComplete and onAbort for router.push and came up with the following method. Everything work as expected except that I am getting an error in the console.
console error
[vue-router] uncaught error during route navigation:
TypeError: onComplete is not a function
goto method
goTo(routeName) {
let self = this
self.$nextTick( function() {
// Show Vue Loading Layer
let loader = this.$loading.show({
loader: 'spinner',
color: '#e8b30f',
backgroundColor: '#000',
opacity: 0.5,
})
// Retrieve new page
self.$router.push(
// First param : location
{name: routeName},
// Second param : onComplete
setTimeout(() => loader.hide(), 3 * 1000)
)
})
}
Even though I got the expected result, I still want to know why that error appears on the console.
Second, is there a better way of doing this? The only reason I used setTimeout() is the new page was loaded too fast for the loading overlay to be shown properly without setting the setTimeout().
Thank you for any help or tips.
The second param to .push expects a function, what you have passed is
setTimeout(() => loader.hide(), 3 * 1000)
which returns an integer, as you can read here: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Return_value
To fix it, you can wrap it inside a function
() => {
setTimeout(() => loader.hide(), 3 * 1000)
}
So you goto method will look like
goTo(routeName) {
let self = this
self.$nextTick( function() {
// Show Vue Loading Layer
let loader = this.$loading.show({
loader: 'spinner',
color: '#e8b30f',
backgroundColor: '#000',
opacity: 0.5,
})
// Retrieve new page
self.$router.push(
// First param : location
{name: routeName},
// Second param : onComplete
() => setTimeout(() => loader.hide(), 3 * 1000)
)
})
}
I am trying to upload an image with react with this code
<input type="file" id="InputFile" accept="image/*" value={this.state.image} onChange={this.handelChangeImage} />
this is the state
constructor(props) {
super(props);
this.state = {
image: [],
}
this.handelChangeImage = this.handelChangeImage.bind(this);
}
and this is the binding function
handelChangeImage(event) {
let image = event.target.files[0];
let form = new FormData();
form.append('image', image);
this.setState({
image: form,
});
}
it gives me that error
Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
any help!!
My error was here
<input type="file" id="InputFile" accept="image/*" value={this.state.image} onChange={this.handelChangeImage} />
I have to remove this value={this.state.image}
Looks like you are not reading your file properly, try something like:
handelChangeImage(event) {
const reader = new FileReader();
reader.onload = function(ee) {
this.setState({fileData: ee.target.result});
}.bind(this);
}
You can find more info about FileReader here.
I've a div (id: dest) in which content is loaded via ajax using query.
I need to compile with angular the content of that div.
Here is the content loaded via ajax:
{{myvar}}
I tried to do:
var app = angular.module("myapp", []);
app.controller("myctrl", function($scope){
$scope.myvar = "hello world!";
});
$("#dest").attr("data-ng-app", "myapp");
$("#dest").attr("data-ng-controller", "myctrl");
console.log(angular.element($("#dest")).size()); //always returns '1'
angular.element($("#dest")).injector().invoke(function($compile) {
var scope = angular.element($("#dest")).scope();
$compile($("#dest"))(scope);
});
I'm sure that $("#dest") exists in dom when that code is executed but
angular.element($("#dest")).injector() is always undefined.
I also tried to wait 15 seconds:
setTimeout(function(){
angular.element($("#dest")).injector().invoke(function($compile) {
var scope = angular.element($("#dest")).scope();
$compile($("#dest"))(scope);
});
}, 15000);
But the problem remains.
PS.
I cannot interact directly with ajax request or response and the div (with id dest), exists at page loading.
Have you tried wrapping that code with $(document).ready() or angular's equivalent:
angular.element(document).ready(function () {
angular.element($("#dest")).injector().invoke(function($compile) {
var scope = angular.element($("#dest")).scope();
$compile($("#dest"))(scope);
});
});
Working jsfiddle.
Have you tried this :
angular.element($("#dest")).injector().invoke(['$compile', function($compile) {
var scope = angular.element($("#dest")).scope();
$compile($("#dest"))(scope);
}]);