How to get rendered height of stackpanel without actually displaying it (Xamarin.Forms) - wpf

I have implemented a custom AccordionControl.
It alternatingly consists of a HeaderView followed by a StackLayout.
When a HeaderView is tapped the corresponding StackLayout is expanded using an animation.
For the animation I need a start point and an end point (the rendered height). I have currently implemented a workaround, which shortly displays the StackLayout, gets the rendered height and then animates it. (The result obviously is a not so smooth animation)
Animation-workaround
stackLayout.IsVisible = true;
double heightWhenRendered = stackLayout.Height;
var animation = new Animation(d => stackLayout.HeightRequest = d, start: 0, end: heightWhenRendered, easing: Easing.CubicInOut);
this._runningAnimations.Add(animation);
animation.Commit(stackLayout, "Expand", finished: (x,s) =>
{
this._runningAnimations.Remove(animation);
});

Related

FabricJS object is not selectable/clickable/resizable in top left corner (React + FabricJs)

I have FabricJs canvas. And I have multiple objects outside canvas. When I click on object, it appears in Canvas. I am not doing anything else with this object programmatically.
Here is part of code that add image to Canvas (it is React code):
const { state, setState } = React.useContext(Context);
const { canvas } = state;
...
const handleElementAdd = (event, image) => {
const imageSize = event.target.childNodes[0].getBoundingClientRect();
const imageUrl = `/storage/${image.src}`;
new fabric.Image.fromURL(imageUrl, img => {
var scale = 1;
if (img.width > 100) {
scale = 100/img.width;
}
var oImg = img.set({
left: state.width / 2 - img.width * scale,
top: state.height / 2 - img.height * scale,
angle: 0})
.scale(scale);
canvas.add(oImg).renderAll.bind(canvas);
canvas.setActiveObject(oImg);
});
};
...
<div
key={img.name}
onClick={(e) => handleElementAdd(e, img)}
className={buttonClass}
>
It appears, I can drag and drop, resize, select it. However, when my object is placed in top left corner of canvas, it is not clickable, resizable etc. I can select larger area and then object is selected, but it doesn't respond to any events like resize, select etc.
Not sure where to look at.
I read that if coordinates are changed programmatically object.setCoord() to object can help, but in this case I do not understand where to put it.
You have to put (call) object.setCoords() each time you have modified the object, or moved the object. So, you can call it on mouseUp event of the canvas.
You can write a function naming "moveObject" in which you get the object you are trying to move/moved/updated. And then just selectedObject.setCoords().
Then call that function inside canvas.on('selection:updated', e => {----})

jsPDF muliple pages with div height - unable to determine the height

I am trying to generate a pdf from html divs with dynamic height and width. Below is the code.
let pages = this.rootDataContainer.nativeElement.getElementsByClassName('pdfpage');
for (let i = 0; i < pages.length; i++) {
this.pdfDoc.addHTML(pages[i], 0, 0, options, () => {
let pxHeight = pages[i].offsetHeight / scaleFactor;
this.pdfDoc.addPage(0, pxHeight);
this.counter = this.counter - 1;
});
}
There are couple of issues I am facing.
As addHTML is async, pages are added to pdf in random way.
height of the pdf page is either getting more or less height.
is there any way to set pdf size and sync the order of pages.
To sync I would use a recursive approach as in:
EDITED:
var pages = document.getElementsByClassName("pdfpage");
var pdfDoc = new jsPDF('p', 'pt', [0, 0]);
pdfDoc.deletePage(1);
pdfDoc.addPage(pages[0].clientWidth, pages[0].clientHeight);
var addDivToPdf = function(pageNr) {
pdfDoc.addHTML(pages[pageNr], 0, 0, {background:"#FFF"}, () => {
if (++pageNr < pages.length) {
//New added DIV dimensions here
pdfDoc.addPage(pages[pageNr].clientWidth, pages[pageNr].clientHeight);
addDivToPdf(pageNr);
} else {
pdfDoc.save("sample-file.pdf");
}
});
};
Notice I haven't use a for loop. This way the next page is added only when the previous is complete.
For the height, I'm not sure, scaleFactor must have the wrong units. It is not really clear if you want all pages to have the same size or you want different sizes to match the DIV height.
UPDATE: To control the widht and height according with the DIVs sizes, I have indicated 'pt' in the pdfDoc constructor:
var pdfDoc = new jsPDF('p', 'pt', [0, 0]);
However the first page appears to have always an standard size, so I have deleted it and added again with the desired size before adding the first DIV. The other pages can follow the normal flow:
pdfDoc.deletePage(1);
pdfDoc.addPage(pages[0].clientWidth, pages[0].clientHeight);
The CodePen is updated:
https://codepen.io/zameb/pen/BdbEVr

Problems related to a resizable component

I am writing a component that allows the width of it's child element to change if you click on its right border and drag it.
I have a few problems however. First off, it's awkward to drag the div element, because if the mouse enters the other element to the right while dragging, the dragging state is lost and bugs out.
Also, I currently show the resize cursor when the point is within 5 pixels of the right border, which works fine when inside the resizable div. However, if you approach the border from the right (mouse inside other div), you cannot select it, even though you're within 5 pixels.
Another problem is that when I drag the mouse and resize the div, the mouse selects the text it drags over.
Lastly, because the element has to rerender each time it's width is changed, I've noticed that the performance is not always smooth.
Any advice on how to mitigate these problems?
Resizable = React.createClass({
propTypes: {
id : React.PropTypes.string,
class : React.PropTypes.string,
width : React.PropTypes.number,
onResize : React.PropTypes.func,
onAction : React.PropTypes.func,
},
getInitialState: function() {
return {
showResizeCursor : false,
canResize : false,
};
},
getDefaultProps: function() {
return {
};
},
_handleMouseMove: function(event) {
var node = React.findDOMNode(this);
var offsets = node.getBoundingClientRect();
var divLeft = offsets.left;
var divRight = offsets.right;
var mouseX = event.clientX;
var maxWidth = this.props.maxWidth || this.props.width;
var minWidth = this.props.minWidth || this.props.width;
var newWidth = mouseX - divLeft + 200;
var isWithinBounds = newWidth <= maxWidth && newWidth >= minWidth;
if (this.state.canResize && isWithinBounds) {
this.props.onResize(newWidth);
}
var difference = Math.abs(divRight - mouseX);
if (difference < 4) {
return this.setState({ showResizeCursor: true });
}
if (this.state.showResizeCursor) {
this.setState({ showResizeCursor: false });
}
},
_handleMouseUp: function() {
this.setState({ canResize: false });
},
_handleMouseDown: function() {
if (this.state.showResizeCursor) {
this.setState({ canResize: true });
}
},
render: function() {
var style = {
width : this.state.width,
};
if (this.state.showResizeCursor) { style.cursor = 'col-resize'; }
return (
<div id={this.props.id}
style ={style}
className ={this.props.class}
onMouseDown ={this._handleMouseDown}
onMouseUp ={this._handleMouseUp}
onMouseMove ={this._handleMouseMove}
onMouseLeave={this._handleMouseUp}
>
{this.props.children}
</div>
);
}
});
Example usage:
render: function() {
...
return (
<Wrapper>
<Resizable
id = {'list-view'}
width = {this.state.listViewWidth}
maxWidth = {this.state.listViewMaxWidth}
minWidth = {this.state.listViewMinWidth}
onResize = {this._handleListViewResize}
>
{first_column_that_should_be_resizable}
</Resizable>
{second_column_not_resizeable}
There are many different concerns here...
First off, it's awkward to drag the div element, because if the mouse enters the other element to the right while dragging, the dragging state is lost and bugs out.
This is a very common issue when you start coding your first drag&drop alike behavior. You should not listen the mousedown, mousemove and mouseup events on the same element, you should only listen the mousedown event and in that handler start listening the other two but on the body of the document. This way, you have a global handler and you will not have problems with the mouse getting over other elements.
Also, I currently show the resize cursor when the point is within 5 pixels of the right border, which works fine when inside the resizable div. However, if you approach the border from the right (mouse inside other div), you cannot select it, even though you're within 5 pixels.
I would suggest you to use only CSS for this. Is what it is for :)
Another problem is that when I drag the mouse and resize the div, the mouse selects the text it drags over.
Yep, just CSS. Once your mousedown handler is executed add a special CSS class to your element and put something like this in your CSS.
.disable-select {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Lastly, because the element has to rerender each time it's width is changed, I've noticed that the performance is not always smooth.
I don't think React is your best option in here. I would just add this behavior using jQuery and the lifecycle methods like componentDidMount. This way, you can resize the div using plain jQuery (on every mouse move) and then just apply the final state (that is, the final size) to your component on the mouseup handler.

AngularJS animate dynamic margin

I have an element that appears when the user clicks a button elsewhere on the screen. The element appears to come out of the top of the screen. The element by default needs to be tucked out of view above the screen, so I will have a margin-top style that is based on the height of the element (and will be a negative value). This cannot be hardcoded in css because the element height may vary. When I click the button, I want the element margin-top to change to 0 and I want a transition animation.
The sample shown on angularJS documentation is for adding a removing a class. This would work fine if I knew the values to be set and could code them in CSS, however I cannot. What is the correct way to solve this?
The code below works for displaying and hiding my element using a margin but there is no animation. How do I trigger an animation here when the margin changes?
https://docs.angularjs.org/guide/animations
Quote Total: {{salesPriceTotal + taxesTotal - tradeInsTotal | currency}}
<div class="totals" ng-style="setTopMargin()">
// totals stuff here.
</div>
$scope.setTopMargin = function() {
return {
marginTop: $scope.marginTop
}
};
$scope.$watch('showTotals', function() {
var margin = $scope.showTotals ? 10 : -160 + $scope.modelTotals.length * -200;
$scope.marginTop = margin.toString() + 'px';
});
I added the following code per a suggested solution, but this code is never hit.
myApp.animation('.totals', function () {
return {
move: function (element, done) {
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function (isCancelled) {
if (isCancelled) {
jQuery(element).stop();
}
}
},
}
});
As the documentation explains: "The same approach to animation can be used using JavaScript code (jQuery is used within to perform animations)".
So you basically needs to use animate() from jQuery to do what you want.

Responsive sticky sidebar, need to update width

I've got a responsive sticky sidebar started here: http://codepen.io/cmegown/pen/fjqzH. I've got the sticky part down, and it's responsive in relation to the width of the original viewport width. However, if you scroll down and then change the viewport size you'll see that the width of the sidebar does not change.
I know I need to update the sidebarWidth variable, but I'm sure exact how/where to do that.
Any help is appreciated, thanks!
EDIT:
This one kind of got left behind amid some other projects, but I'm back to finish this. I got a little further, but still can't seem to figure out how to update the sidebar width if the user scrolls down the page then expands their browser (or rotates their device). I have some commented code in the JS panel where I left off.
Just put the width calculation in your scroll function.
$(function () {
// cache selectors
var wrapper = $('.wrapper');
var sidebar = $('.sidebar');
// get some maths
var sidebarTop = sidebar.offset().top;
var sidebarOffset = 25; // is .wrapper padding
// sticky logic
$(window).on('scroll', function () {
var windowTop = $(window).scrollTop();
var sidebarWidth = Math.round(wrapper.width() * 0.3); // is .sidebar width
if (sidebarTop < (windowTop + sidebarOffset)) {
sidebar.css({
position: 'fixed',
top: sidebarOffset,
width: sidebarWidth
})
} else {
sidebar.css({
position: 'static',
width: '30%' // original CSS value
})
}
})
})
There's a little bit of overhead there, since it has to calculate the width every time you scroll. The alternative would be to put it into a $(window).resize() function so it figures out the width when you resize the window.

Resources