Setting Column Widths using Javascript - javascript

I'm Trying to Figure out how to set column widths and heights using javascript so that i can set them according to the browser window. i figured the following code. but its not working. please help
<script type="javascript">
function size()
{
document.getElementById("body").style.width=window.innerWidth;
document.getElementById("body").style.height=window.innerHeight;
var width=document.getElementById("body").style.width;
var height=document.getElementById("body").style.height;
document.getElementById("header").style.height=(.15*height);
document.getElementById("loginbox").style.height=(.15*height);
document.getElementById("navigation").style.height=(.05*height);
document.getElementById("leftpanel").style.height=(.70*height);
document.getElementById("contentarea").style.height=(.75*height);
document.getElementById("addcolumn").style.height=(.75*height);
document.getElementById("footer").style.height=(.10*height);
document.getElementById("header").style.width=(.75*width);
document.getElementById("loginbox").style.width=(.25*width);
document.getElementById("navigation").style.width=(width);
document.getElementById("leftpanel").style.width=(.20*width);
document.getElementById("contentare").style.width=(.65*width);
document.getElementById("addcolumn").style.width=(.15*width);
document.getElementById("footer").style.width=(width);
}
</script>
i use css to float the columns according to requirement.
and then i'm calling the function size() in body ( onload=size() )
but not working

style attributes are strings and need a unit in the end:
document.getElementById("addcolumn").style.width=(width + "px");
Chances are you've also need to set width and height as:
var width = window.innerWidth;
var height = window.innerHeight

I would suggest a different way to accomplish this, which will be easily mantainable:
function size() {
var props = {
header: {
width: .75,
height: .15
},
loginbox: {
width: .25,
height: .15
},
navigation: {
width: 1,
height: .05
},
leftpanel: {
width: .2,
height: .7
},
contentarea: {
width: .65,
height: .75
},
addcolumn: {
width: .15,
height: .75
},
footer: {
width: 1,
height: .1
}
},
bodyElement = document.getElementById('body'),
width = bodyElement.style.width = window.innerWidth,
height = bodyElement.style.height = window.innerHeight;
for (var id in props) {
if (!props.hasOwnProperty(id)) continue;
var element = document.getElementById(id),
prop = props[id];
element.width = (width * prop.width) + "px";
element.height = (height * prop.height) + "px";
}
}
Note: I haven't tested it, but it should work.

Why not use simple css, make the width a percentage,
width: 75%;

Related

Zoom towards are mouse is over

I have found this script online which i though would help me to zoom in on an image, but zoom towards the mouse position. However when the mouse is positioned to the left this isn't the case. I feel like there is a simple change I need to make however I can't find it!
window.addEventListener('load', () => {
new Vue({}).$mount('#app');
});
Vue.component('test', {
template: '#template',
data() {
return {
zoomMin: 1,
zoomMax: 7,
dragEventX: null,
dragEventY: null,
touchEvent: null,
zoomPointX: 0,
zoomPointY: 0,
zoomScale: 1,
zoomStyle: null,
frame: 1,
speed: 1,
zoom: 1,
}
},
mounted() {
this.$refs.image.addEventListener('wheel', this.onWheel);
},
methods: {
onWheel($event) {
$event.preventDefault();
let direction = Math.sign($event.deltaY);
let n = this.zoomScale - direction / (6 / this.speed);
this.setZoomScale($event.clientX, $event.clientY, n)
},
setZoomScale(clientX, clientY, n) {
const bounding = this.$refs.image.getBoundingClientRect();
let mouseLeft = clientX - bounding.left,
mouseTop = clientY - bounding.top,
zoomPointX = this.zoomPointX || 0,
zoomPointY = this.zoomPointY || 0;
let leftPoint = (mouseLeft - zoomPointX) / this.zoomScale,
topPoint = (mouseTop - zoomPointY) / this.zoomScale;
this.zoomScale = Math.min(Math.max(n, this.zoomMin), this.zoomMax);
let leftZoom = -leftPoint * this.zoomScale + mouseLeft,
topZoom = -topPoint * this.zoomScale + mouseTop;
this.setZoomPoint(leftZoom, topZoom);
},
setZoomPoint(leftZoom, topZoom) {
let left = leftZoom || this.zoomPointX || 0,
top = topZoom || this.zoomPointY || 0,
leftOffset = this.$el.clientWidth * (this.zoomScale - 1),
topOffset = this.$el.clientHeight * (this.zoomScale - 1);
this.zoomPointX = Math.min(Math.max(left, -leftOffset), 0);
this.zoomPointY = Math.min(Math.max(top, -topOffset), 0);
this.setZoomStyle();
},
setZoomStyle() {
this.zoomStyle = {
transform: `translate(${this.zoomPointX}px, ${this.zoomPointY}px) scale(${this.zoomScale})`
};
},
}
});
#app {
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 80%;
overflow: hidden;
border: 1px solid #000;
}
.container img {
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test></test>
</div>
<script type="text/x-template" id="template">
<div class="container">
<img ref="image" :style="zoomStyle" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/field/image/2020_YAM_YZF1000R1_EU_DPBMC_STA_001-70516%20copy.jpg"></img>
</div>
</script>
You do not zoom towards the mouse on the right either; in fact, if you put your mouse in the top-left corner you zoom in to the center of the image. Besides that I can at least identify that this.zoomPointX = Math.min(Math.max(left, -leftOffset), 0); only allows your translate to go left, and thus only allow zooming towards the right of the center line.
Some debugging
Let's start with taking a step back and figuring out what we are doing here. The final styling is a translate, followed by a scale. The scale happens around the center of what you are seeing on screen right then, and the translate that happens before that is meant to move the image so the point around which you want to zoom is in the middle.
To properly debug this we need a better understanding of what the code is doing, so I first added some debug markers and disabled the zoom styling, so we can visualise what is happening without it zooming all over the place.
window.addEventListener('load', () => {
new Vue({}).$mount('#app');
});
Vue.component('test', {
template: '#template',
data() {
return {
zoomMin: 1,
zoomMax: 7,
dragEventX: null,
dragEventY: null,
touchEvent: null,
zoomPointX: 0,
zoomPointY: 0,
zoomScale: 1,
zoomStyle: null,
frame: 1,
speed: 1,
zoom: 1,
// Debugging
imageHeight: 0,
imageWidth: 0,
}
},
computed: {
markerStyle() {
return {
top: `${this.imageHeight / 2 - this.zoomPointY}px`,
left: `${this.imageWidth / 2 - this.zoomPointX}px`,
};
},
boundaryMarkerStyle() {
const middleY = this.imageHeight / 2 - this.zoomPointY;
const middleX = this.imageWidth / 2 - this.zoomPointX;
const height = this.imageHeight / this.zoomScale;
const width = this.imageWidth / this.zoomScale;
return {
top: `${middleY - height / 2}px`,
left: `${middleX - width / 2}px`,
width: `${width}px`,
height: `${height}px`,
};
},
},
mounted() {
// Moved the listener to the container so we can overlay something over the image
this.$refs.container.addEventListener("wheel", this.onWheel);
// Temporary for debugging; I could also determine it dynamically and should if we
// we want to be able to resize
this.$refs.image.addEventListener("load", () => {
const bounding = this.$refs.image.getBoundingClientRect();
this.imageHeight = bounding.height;
this.imageWidth = bounding.width;
});
},
methods: {
onWheel($event) {
$event.preventDefault();
let direction = Math.sign($event.deltaY);
let n = this.zoomScale - direction / (6 / this.speed);
this.setZoomScale($event.clientX, $event.clientY, n)
},
setZoomScale(clientX, clientY, n) {
const bounding = this.$refs.image.getBoundingClientRect();
// mouseLeft and mouseTop represent the pixel within the container we are targeting
const mouseLeft = clientX - bounding.left;
const mouseTop = clientY - bounding.top;
// zoomPointX and Y represent what point we were zooming towards
const zoomPointX = this.zoomPointX || 0;
const zoomPointY = this.zoomPointY || 0;
// This attempts to modify the point we are targeting based on
// what we are zooming towards before and what we are zooming towards now
// zoomPointX represents something that is calculated with a different zoomScale, so this
// presumably calculates bogus
const leftPoint = (mouseLeft - zoomPointX) / this.zoomScale;
const topPoint = (mouseTop - zoomPointY) / this.zoomScale;
// This normalizes the zoom so we can't zoom out past the full image and not past 7 times the current image
this.zoomScale = Math.min(Math.max(n, this.zoomMin), this.zoomMax);
// This should represent the point we are zooming towards (I think?)
const leftZoom = -leftPoint * this.zoomScale + mouseLeft;
const topZoom = -topPoint * this.zoomScale + mouseTop;
// This function breaks its promise to set only the zoom scale and also sets the zoom point. :(
this.setZoomPoint(leftZoom, topZoom);
},
setZoomPoint(leftZoom, topZoom) {
const left = leftZoom || this.zoomPointX || 0;
const top = topZoom || this.zoomPointY || 0;
const leftOffset = this.$el.clientWidth * (this.zoomScale - 1);
const topOffset = this.$el.clientHeight * (this.zoomScale - 1);
this.zoomPointX = Math.min(Math.max(left, -leftOffset), 0);
this.zoomPointY = Math.min(Math.max(top, -topOffset), 0);
this.setZoomStyle();
},
setZoomStyle() {
// this.zoomStyle = {
// transform: `translate(${this.zoomPointX}px, ${this.zoomPointY}px) scale(${this.zoomScale})`
// };
},
}
});
#app {
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 80%;
overflow: hidden;
border: 1px solid #000;
position: relative;
}
.container img {
width: 100%;
}
.marker {
position: absolute;
border: 8px solid red;
z-index: 9999;
}
.boundary-marker {
position: absolute;
border: 2px dashed red;
z-index: 9999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test></test>
</div>
<script type="text/x-template" id="template">
<div class="container" ref="container">
<img ref="image" :style="zoomStyle" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/field/image/2020_YAM_YZF1000R1_EU_DPBMC_STA_001-70516%20copy.jpg"></img>
<div class="marker" :style="markerStyle"></div>
<div class="boundary-marker" :style="boundaryMarkerStyle"></div>
</div>
</script>
Fixing the center point
First of all, lets disable the functionality to clamp the zoom point, as we know it to be broken.
this.zoomPointX = left;
this.zoomPointY = top;
Then lets focus on getting the center point right. To get this right, we need to actually determine which pixel of the original image we are targeting, taking into account we might have zoomed in already! We need to keep in mind that the translate function and the scale function always are on the original image.
We can determine the part of the image we are currently viewing with zoomPointX, zoomPointY and zoomScale. (hint: we did that for the debug marker already) zoomPointX and zoomPointY do not really represent a point we zoom towards, but more the translation we made, so I have renamed them to translateX and translateY for convenience.
The x coordinate of the pixel in the original picture we can currently see on the left side of the screen is calculated by finding the x coordinate of the middle point on the original image, then subtracting half of our viewport from it:
const leftSideX = (this.imageWidth / 2 - this.translateX) - (this.imageWidth / this.zoomScale / 2);
The number of pixels from the left side of our viewport to the point, as it would be on the original picture can be calculated by multiplying the amount of pixels this viewport represents with the percentage of pixels from the left border we are
const offsetX = (this.imageWidth / this.zoomScale) * (mouseLeft / this.imageWidth);
And then we get our translateX by calculating from the middle of the image again.
this.translateX = -(leftSideX + offsetX - (this.imageWidth / 2));
window.addEventListener('load', () => {
new Vue({}).$mount('#app');
});
Vue.component('test', {
template: '#template',
data() {
return {
zoomMin: 1,
zoomMax: 7,
dragEventX: null,
dragEventY: null,
touchEvent: null,
translateX: 0,
translateY: 0,
zoomScale: 1,
zoomStyle: null,
frame: 1,
speed: 1,
zoom: 1,
// Debugging
imageHeight: 0,
imageWidth: 0,
}
},
computed: {
markerStyle() {
return {
top: `${this.imageHeight / 2 - this.translateY}px`,
left: `${this.imageWidth / 2 - this.translateX}px`,
};
},
boundaryMarkerStyle() {
const middleY = this.imageHeight / 2 - this.translateY;
const middleX = this.imageWidth / 2 - this.translateX;
const height = this.imageHeight / this.zoomScale;
const width = this.imageWidth / this.zoomScale;
return {
top: `${middleY - height / 2}px`,
left: `${middleX - width / 2}px`,
width: `${width}px`,
height: `${height}px`,
};
},
},
mounted() {
// Moved the listener to the container so we can overlay something over the image
this.$refs.container.addEventListener("wheel", this.onWheel);
// Temporary for debugging; I could also determine it dynamically and should if we
// we want to be able to resize
this.$refs.image.addEventListener("load", () => {
const bounding = this.$refs.image.getBoundingClientRect();
this.imageHeight = bounding.height;
this.imageWidth = bounding.width;
});
},
methods: {
onWheel($event) {
$event.preventDefault();
const direction = Math.sign($event.deltaY);
const scale = this.zoomScale - direction / (6 / this.speed);
this.setZoomScale($event.clientX, $event.clientY, scale);
},
setZoomScale(clientX, clientY, scale) {
const bounding = this.$refs.image.getBoundingClientRect();
// mouseLeft and mouseTop represent the pixel within the container we are targeting
const mouseLeft = clientX - bounding.left;
const mouseTop = clientY - bounding.top;
// translateX and Y represent the translation towards the point we are zooming towards
const leftSideX =
this.imageWidth / 2 -
this.translateX -
this.imageWidth / this.zoomScale / 2;
const offsetX =
(this.imageWidth / this.zoomScale) * (mouseLeft / this.imageWidth);
this.translateX = -(leftSideX + offsetX - this.imageWidth / 2);
const leftSideY =
this.imageHeight / 2 -
this.translateY -
this.imageHeight / this.zoomScale / 2;
const offsetY =
(this.imageHeight / this.zoomScale) * (mouseTop / this.imageHeight);
this.translateY = -(leftSideY + offsetY - this.imageHeight / 2);
// This normalizes the zoom so we can't zoom out past the full image and not past 7 times the current image
this.zoomScale = Math.min(Math.max(scale, this.zoomMin), this.zoomMax);
},
}
});
#app {
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 80%;
overflow: hidden;
border: 1px solid #000;
position: relative;
}
.container img {
width: 100%;
}
.marker {
position: absolute;
border: 8px solid red;
z-index: 9999;
}
.boundary-marker {
position: absolute;
border: 2px dashed red;
z-index: 9999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test></test>
</div>
<script type="text/x-template" id="template">
<div class="container" ref="container">
<img ref="image" :style="zoomStyle" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/field/image/2020_YAM_YZF1000R1_EU_DPBMC_STA_001-70516%20copy.jpg"></img>
<div class="marker" :style="markerStyle"></div>
<div class="boundary-marker" :style="boundaryMarkerStyle"></div>
</div>
</script>
Fixing the bounds and doing cleanup
The original min-max function that we removed earlier was, I think meant to prevent you from zooming to a point where you see white on the outside of the image (ala what did happen in the original when you zoomed on the bottom right).
We can do this by clamping the value of translateX and translateY to an imaginary rectangle that is half the width/half the height from each of the relevant edges. We start by determining the height/width of our viewport (hint: we already calculated this for the marker). The center is (0, 0), while the edges are variations of (+/- imageWidth / 2, +/- imageHeight / 2).
Afterwards, we just need to clamp the value.
const viewportHeight = this.imageHeight / this.zoomScale;
const viewportWidth = this.imageWidth / this.zoomScale;
const exclusionViewportY = (this.imageHeight / 2) - (viewportHeight / 2);
const exclusionViewportX = (this.imageWidth / 2) - (viewportWidth / 2);
this.translateX = Math.min(Math.max(this.translateX, -exclusionViewportX), exclusionViewportX);
this.translateY = Math.min(Math.max(this.translateY, -exclusionViewportY), exclusionViewportY);
Finally, I took the liberty to move zoomStyle to a computed property. It saves on having to call functions in methods that have nothing to do with that method like you did. I renamed the main function to better represent what it does. I also added a beforeDestroy lifecycle hook, because your code currently leaks memory as the event handler is not removed.
window.addEventListener('load', () => {
new Vue({}).$mount('#app');
});
Vue.component('test', {
template: '#template',
data() {
return {
zoomMin: 1,
zoomMax: 7,
dragEventX: null,
dragEventY: null,
touchEvent: null,
translateX: 0,
translateY: 0,
zoomScale: 1,
frame: 1,
speed: 1,
zoom: 1,
};
},
computed: {
zoomStyle() {
return {
transform: `translate(${this.translateX}px, ${this.translateY}px) scale(${this.zoomScale})`,
};
},
},
mounted() {
// Moved the listener to the container so we can overlay something over the image
this.$refs.container.addEventListener("wheel", this.onWheel);
},
methods: {
onWheel($event) {
$event.preventDefault();
const direction = Math.sign($event.deltaY);
const scale = this.zoomScale - direction / (6 / this.speed);
this.calculateZoom($event.clientX, $event.clientY, scale);
},
calculateZoom(clientX, clientY, scale) {
const bounding = this.$refs.image.getBoundingClientRect();
// mouseLeft and mouseTop represent the pixel within the container we are targeting
const mouseLeft = clientX - bounding.left;
const mouseTop = clientY - bounding.top;
// translateX and Y represent the translation towards the point we are zooming towards
const leftSideX =
bounding.width / 2 -
this.translateX -
bounding.width / this.zoomScale / 2;
const offsetX =
(bounding.width / this.zoomScale) * (mouseLeft / bounding.width);
this.translateX = -(leftSideX + offsetX - bounding.width / 2);
const leftSideY =
bounding.height / 2 -
this.translateY -
bounding.height / this.zoomScale / 2;
const offsetY =
(bounding.height / this.zoomScale) * (mouseTop / bounding.height);
this.translateY = -(leftSideY + offsetY - bounding.height / 2);
// This normalizes the zoom so we can't zoom out past the full image and not past 7 times the current image
this.zoomScale = Math.min(Math.max(scale, this.zoomMin), this.zoomMax);
// Finally, we clamp the center point so we always stay within the image
const viewportHeight = bounding.height / this.zoomScale;
const viewportWidth = bounding.width / this.zoomScale;
const exclusionViewportY = bounding.height / 2 - viewportHeight / 2;
const exclusionViewportX = bounding.width / 2 - viewportWidth / 2;
this.translateX = Math.min(
Math.max(this.translateX, -exclusionViewportX),
exclusionViewportX
);
this.translateY = Math.min(
Math.max(this.translateY, -exclusionViewportY),
exclusionViewportY
);
},
}
});
#app {
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 80%;
height: 80%;
overflow: hidden;
border: 1px solid #000;
position: relative;
padding: 0;
margin: 0;
line-height: 0;
}
.container img {
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<test></test>
</div>
<script type="text/x-template" id="template">
<div class="container" ref="container">
<img ref="image" :style="zoomStyle" src="https://s3-eu-west-1.amazonaws.com/crash.net/visordown.com/field/image/2020_YAM_YZF1000R1_EU_DPBMC_STA_001-70516%20copy.jpg"></img>
</div>
</script>

How to center fixed width table on page horizontally? jsPDF with autotable

I'm using jsPDF with autotable, I'm trying to center the table with fixed-width on generated pdf page. I can't seem to find a solution for this, everywhere I look is just full width, I don't want my table to be full width. I tried with margins option but It's not very precise and I'm not entirely sure how to calculate margins from A4 to horizontally center my table.
Screenshot:
document.querySelector(".report-btn").addEventListener("click", () => {
const pdfDoc = new jsPDF();
const list = createListforPdf(); // function to generate list
pdfDoc.autoTable({
theme: "grid",
tableWidth: 100,
styles: { halign: "center" },
columnStyles: {
0: { cellWidth: 85, halign: "left" },
1: { cellWidth: 15 }
},
head: [["MRZ Check number", "is OK?"]],
body: [[list[0], "YES"], [list[1], "NO"]]
});
pdfDoc.save();
});
You can calculate the margins in the following way:
let doc = new jsPDF();
let wantedTableWidth = 100;
let pageWidth = doc.internal.pageSize.width;
let margin = (pageWidth - wantedTableWidth) / 2;
doc.autoTable({
head: [['Name', 'Country']],
body: [
['Leon', 'Sweden'],
['Magnus', 'Norway'],
['Juan', 'Argentina'],
],
margin: {left: margin, right: margin}
});
doc.save('table.pdf');
I solved this problem like this:
var pageWidth = receiptPDF.internal.pageSize.width || receiptPDF.internal.pageSize.getWidth(); var wantedTableWidth = 60;
var marginTable = (pageWidth - wantedTableWidth) / 2
receiptPDF.autoTable({
margin: {top: 0, right: marginTable, left: marginTable, bottom: 0 },
cellWidth: 'auto',
minCellHeight: 2
}
Please note, that the cellWidth property has to be set to 'auto' in order to work.
Wrap the table with a div element.
<div class="table-container">
<table id="my-table"><!--...--></table>
</div>
In CSS, write the class to center the table within the container.
div.table-container {
display: flex;
justify-content: center;
}

How to get Text object font-size after modifying object

How can I get Text object font size after modifying object in fabric.js?
Below is my code.
var text = new fabric.Text(imgText, {
left: 10,
top: 5,
fontSize: 15,
fontFamily: 'Verdana',
fill: 'white'
});
text.scaleToWidth(canvas.width * 0.5);
text.setControlsVisibility(canvasConfig);
canvas.add(text);
canvas.renderAll();
var objects = canvas.getActiveObject();
var obj = objects;
if (!obj) {
return;
}
//console.log(obj.get('fontSize') *= obj.scaleX);
var angle = obj.get('angle');
var objWidth = obj.get('width') * obj.scaleX;
var objWidthPercent = objWidth / canvas.width * 100;
var objHeight = obj.get('height') * obj.scaleY;
var objHeightPercent = objHeight / canvas.height * 100;
var bound = obj.getBoundingRect();
var objLeft = obj.get('left') / canvas.width * 100;
var objTop = obj.get('top') / canvas.height * 100;
var newfontsize = obj.fontSize * obj.scaleX;
Above I set default FontSize to 15. then I modify object I can get proper Height, Width, Left, Top, but I am not able to get FontSize.
In backend i set image and text like below screenshot.
In frontend what i get like below screenshot.
Below style for image & text on frontend.
element.style {
left: 64.37%;
top: 14.54%;
width: 28.25%;
height: 14.37%;
font-size: 63.58px;
color: #E346FF;
font-family: Times New Roman;
position: absolute;
max-width: 100%;
z-index: 996;
max-height: 100%;
}
element.style {
left: 56.5%;
top: 0.81%;
width: 42.86%;
height: 42.86%;
max-width: 100%;
max-height: 100%;
position: absolute;
z-index: 995;
display: block;
background-image: url(http://10.16.16.101/LabelExtension/pub/media/labelimageuploader/images/image/o/r/orange_38.png);
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
When i add below code it working perfect but in backend object is getting blur.
this.canvas.setHeight(300);
this.canvas.setWidth(240);
this.canvas.backgroundColor = '#E8EEF1';
this.canvas.setDimensions({width: '480px', height: '600px'}, {cssOnly: true});
Here is the way to match Fabric.js font transforms to CSS transforms: https://jsfiddle.net/mmalex/evpky3tn/
The solution is to match transformations of texts, not trying to adjust font size.
Step 1 - Prepare scene, compose canvas, group text with rectangle, let user manipulate this group, rotate and scale it.
var canvas = new fabric.Canvas(document.getElementById('c'));
var rect = new fabric.Rect({
width: 50,
height: 50,
left: 90,
top: 120,
fill: 'rgba(255,0,0,0.25)'
});
var text = new fabric.Text("123", {
left: rect.left,
top: rect.top,
fontSize: 15,
fontFamily: 'Verdana',
fill: 'black'
});
text.scaleToWidth(rect.width);
canvas.add(text);
var group = new fabric.Group([rect, text], {
originX: 'center',
originY: 'center',
angle: 25,
scaleY: 1.7
});
canvas.add(group);
canvas.renderAll();
Step 2 - prepare DIV style for scaling
.scaled { // this style is applied to DIV element with text
...
font-size: 15px; // Fabric.js text is also 15px size
transform: scale(1, 1); // define initial transform
transition: all 0.25s linear; // let it animate
...
}
Step 3 - Evaluate Fabric.js transforms and apply CSS on DIV element,
for example:
element.style {
transform: rotate(25deg) scale(1.74774, 2.97116);
}
The solution (aka button handler) converts Fabric.js transform to CSS transform:
function matchCssTransform() {
let textScale = text.getTotalObjectScaling();
let pixelRatio = window.devicePixelRatio;
let styleStr = `rotate(${group.angle}deg) scale(${textScale.scaleX / pixelRatio}, ${textScale.scaleY / pixelRatio}) `;
document.getElementById("scaled").style.transform = styleStr;
}
getHeightOfLine(lineIndex) → {Number} : Computes height of character at given position and return fontSize of the character. Is it? tell me.
It's a method of text class. http://fabricjs.com/docs/fabric.Text.html#getHeightOfLine
This is the vanilla js solution.
I am not able to get FontSize.
You can achieve fontSize using the computedStyle like
let style = window.getComputedStyle(text, null).getPropertyValue('font-size');
let fontSize = parseFloat(style);
https://developer.mozilla.org/de/docs/Web/API/Window/getComputedStyle
In addition to this I recommend you to work with vw and vh
.text {
font-size: 10vw;
}
https://web-design-weekly.com/2014/11/18/viewport-units-vw-vh-vmin-vmax/
All together gives us https://jsfiddle.net/a8woL1eh/
The problem is Text object does not change its fontSize property when it is modified or transformed unless set fontSize property manually. It's the reason why you get blurred text because despite changed the transform size the main fontSize remaining same. So possible solution will be somehow changing fontSize property dynamically.
http://jsfiddle.net/therowf/xkhwagd8/41/
Here I have attached a jsfiddle example form your code. This code is very similar to your one.
Thanks and let me know if this is worked. (:
var canvas = new fabric.Canvas(document.getElementById('c'));
var imgText ="This is text";
var canvasConfig = true;
var text = new fabric.Text(imgText, {
left: 10,
top: 5,
fontSize: 50,
fontFamily: 'Verdana',
fill: 'black'
});
text.scaleToWidth(canvas.width * 0.5);
text.setControlsVisibility(canvasConfig);
canvas.add(text);
canvas.renderAll();
var objects = canvas.getActiveObject();
function moveHandler(evt){
//evt.target.fontSize = 10;
var fontSizeX = evt.target.scaleX;
var fontSizeY = evt.target.scaleY;
if(fontSizeX === fontSizeY){
evt.target.fontSize = fontSizeX*100;
console.log(evt.target.fontSize);
}
}
canvas.on('object:scaling', moveHandler);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.6.0/fabric.min.js"></script>
<canvas id="c" width="400" height="400"></canvas>

Trying the native Element.animate method, but cannot add keyframe percentages

Someone asked a related question and I am trying to make it work with the experimental technology Element.animate.
The MDN documentation is rather...slim and the state of the technology is lets say infantile.
https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
What I have is this markup:
<div class="container">
<div id="movetxt">left to right, right to left</div>
</div>
Just want the movetxt div to move from left to right ad infinity within the container div.
Css is hard coded, as I do not know how to get the width of the div text node.
.container {
margin: 0 auto;
width: 300px;
border: 1px solid red;
}
#movetxt {
width: 180px;
}
Now, the JS
var container = document.querySelector(".container");
var movingText = document.getElementById("movetxt");
var containerWidth = container.offsetWidth;
var textWidth = movingText.offsetWidth;
var totalDistance = containerWidth - textWidth;
var oneWayDistance = totalDistance / 2; //just establishing the travel distance
And now the experiment part:
movingText.animate([
// keyframes
{ transform: 'translateX(' + oneWayDistance + 'px)' },
{ transform: 'translateX(-' + totalDistance + 'px)' }
], {
// timing options
duration: 1000,
iterations: Infinity
});
This kind of works, the console throws some errors, but the thing runs.
But, I want to have this kind of keyframas format, just an example:
0% { transform: 'translateX(' + oneWayDistance + 'px)' },
10% { transform: 'translateX(-' + totalDistance + 'px)' }
If I try that, I get Failed to execute 'animate' on 'Element': Keyframes must be objects, or null or undefined.
Link to fiddle:
http://jsfiddle.net/vdb3ofmL/1135/
There must be some way to place the keyframe % values there, can anyone figure this out?
Cheers
You need to consider the fact that the text is initially on the left edge of the container, so you need to translate form 0 to width of container - width of text:
And what you are looking for is the offset valueref
var container = document.querySelector(".container");
var movingText = document.getElementById("movetxt");
var containerWidth = container.offsetWidth;
var textWidth = movingText.offsetWidth;
var totalDistance = containerWidth - textWidth;
var oneWayDistance = totalDistance / 2;
console.log(containerWidth);
console.log(textWidth);
movingText.animate([
// keyframes
{ transform: 'translateX(0)',offset:0 }, /*0%*/
{ transform: 'translateX(' + totalDistance + 'px)',offset:0.3 }, /*30%*/
{ transform: 'translateX(0)',offset:1 } /*100%*/
], {
// timing options
duration: 1000,
iterations: Infinity
});
.container {
margin: 0 auto;
width: 300px;
border: 1px solid red;
}
#movetxt {
width: 180px;
border:1px solid;
}
<div class="container">
<div id="movetxt">left to right, right to left</div>
</div>
You should use offset like this
element.animate({
opacity: [ 0, 0.9, 1 ],
offset: [ 0, 0.8 ], // Shorthand for [ 0, 0.8, 1 ]
easing: [ 'ease-in', 'ease-out' ],
}, 2000);
and in your case you can use like this and change your own
var container = document.querySelector(".container");
var movingText = document.getElementById("movetxt");
var containerWidth = container.offsetWidth;
var textWidth = movingText.offsetWidth;
var totalDistance = containerWidth - textWidth;
var oneWayDistance = totalDistance / 2;
movingText.animate(
{transform:['translateX(' + oneWayDistance + 'px)','translateX(-' + totalDistance + 'px)','translateX(' + oneWayDistance + 'px)'],
offset: [0,0.1]
}, {
duration: 1000,
iterations: Infinity
});
.container {
margin: 0 auto;
width: 300px;
border: 1px solid red;
}
#movetxt {
width: 180px;
}
<div class="container">
<div id="movetxt">left to right, right to left</div>
</div>

Text is vertically stretched on my canvas, making it unreadable

I have a canvas that I am drawing on top of a OpenLayers map. The canvas is colored in gradient colors and now I am trying to add text on top of it.
However the text seems to be stretched making it completely unreadable.
function createSpeedBar(min, max, speeds) {
//List of integer speeds
var fullSpeeds = [];
//List of unique speed values (no duplicates)
var uniqueSpeeds = [];
//Filling fullSpeeds using minimum and maximum values
for (i = min; i <= max; i++) {
fullSpeeds.push(i);
}
//Filling uniqueSpeeds with unique values
$.each(speeds, function (i, el) {
if ($.inArray(el, uniqueSpeeds) === -1) uniqueSpeeds.push(el);
});
//Sorting uniqueSpeeds (low to high)
uniqueSpeeds = uniqueSpeeds.sort(function (a, b) {
return a - b;
});
//Getting canvas element
var canvas = document.getElementById("speedList");
var context = canvas.getContext("2d");
context.rect(0, 0, canvas.width, canvas.height);
var grd = context.createLinearGradient(0, 0, 0, 170);
var hslMin = 0;
var hslMax = 240;
//Defining hslColors using uniqueSpeeds
var hslValues = uniqueSpeeds.map(function (value) {
if ($.inArray(value, fullSpeeds)){
return {
h: Math.ceil(((value - min) / (max - min)) * (hslMax - hslMin) + hslMin),
s: 100,
l: 50,
full: true,
speed: value
};
} else {
return {
h: Math.ceil(((value - min) / (max - min)) * (hslMax - hslMin) + hslMin),
s: 100,
l: 50,
full: false
};
};
});
var count = 1;
var length = hslValues.length;
//Gradient coloring using hslColors
hslValues.forEach(function (value) {
var color = 'hsl(' + value.h + ',' + value.s + '%,' + value.l + '%)';
grd.addColorStop(count / length, color)
count += 1;
});
context.fillStyle = grd;
context.fill();
//Setting up coloring and drawing of text
count = 1
var height = canvas.height;
var width = canvas.width;
var elementHeight = height / length;
//Drawing text on canvas
hslValues.forEach(function (value) {
context.font = "12px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "black";
if (value.full === true) {
context.fillText(value.speed, width / 2, ((elementHeight / 2) + elementHeight * count));
}
count += 1;
});
}
As it might be clear I am trying to create a bar displaying the intensities of the speed on the map where I have colored some markers. However the text on the canvas comes out like this:
Right now I have made the height of the canvas inherit the height of the map which is 500. The width of the canvas is set manually using css:
html
<div id="map" class="map">
<canvas id="speedList"></canvas>
</div>
css
#map {
position: relative;
height: 500px;
}
#speedList {
position: absolute;
right: 10px;
top: 0;
bottom: 0;
width: 20px;
z-index: 1000;
height: inherit;
margin: 0;
padding: 0;
}
I am currently working on a Fiddle, but it takes a little time to reproduce, and I bet the issue is not that big, so hopefully someone knows how to fix it before I finish the Fiddle.
The main problem here is the CSS adjustment of the width and height of the canvas element.
If it helps to understand the problem, think of <canvas> the same way you would think of <img/>, if you take an img, and give it a width and height of 50 x 500, it would stretch too.
The fix is to ensure that you set the width an height of the canvas element itself, before it processes it's content, like this:
<canvas id="speedList" width="20" height="500"></canvas>
You then also need to make sure your remove the width and height properties inside your CSS, like this:
#map {
position: relative;
height: 500px;
}
#speedList {
position: absolute;
right: 10px;
top: 0;
bottom: 0;
z-index: 1000;
margin: 0;
padding: 0;
}

Categories