When positing mapTypeControl (Satellite, Terrain) to BOTTOM_LEFT/BOTTOM_CENTER/BOTTOM_RIGHT, it sets the controls too low, so when clicking on Satellite the sub-option becomes cutoff.
This issue is viewable here:
https://developers.google.com/maps/documentation/javascript/examples/control-positioning
Left and Right side have solution by placing the controls in LEFT_BOTTOM/RIGHT_BOTTOM.
Is it possible to position it a bit higher in the center position?
Thanks
I have run into the same issues. You can find all the available Positions here. You might want to use LEFT_BOTTOM: "Elements are positioned on the left, above bottom-left elements, and flow upwards."
Worst case scenario try LEFT_CENTER position.
+----------------+
+ TL TC TR +
+ LT RT +
+ +
+ LC RC +
+ +
+ LB RB +
+ BL BC BR +
+----------------+
You can override the dropdown to make it appear above the button with the following CSS:
.gm-style-mtc div[role="button"] + div {
top: -90px !important;
}
The top negative px amount will depend on your button size setting (e.g. controlSize: 30).
Create custom buttons like this:
// css
toggleGroup: {
position: 'absolute',
boxShadow: `rgb(0 0 0 / 30%) 0px 1px 4px -1px`,
bottom: '35px',
left: '10px',
'& .MuiToggleButton-root': {
fontSize: '18px',
color: 'rgb(86, 86, 86)',
},
'& .MuiToggleButton-root.Mui-selected': {
color: '#000',
fontWeight: 700
},
}
// toggle method
const handleToggle = (_e: React.MouseEvent<HTMLElement, MouseEvent>, view: ViewType) => {
if (map === null) {
return;
}
if (view !== null) {
setView(view);
view === ViewType.SATELLITE ? map.setMapTypeId('hybrid') : map.setMapTypeId('roadmap');
}
};
// component
<ToggleButtonGroup
value={view}
className={classes.toggleGroup}
onChange={handleToggle}
aria-label='Select Map Type'
exclusive>
<ToggleButton value={ViewType.MAP}>Map</ToggleButton>
<ToggleButton value={ViewType.SATELLITE}>Satellite</ToggleButton>
</ToggleButtonGroup>
Related
The Google Visualization Chart is filled with a color if the fill css property is set, e.g.
* {
fill: black;
}
Is there a way to unset the CSS fill property so that it uses the original colors? The goal is not to modify the Chart component is any way, the fill is unrelated to the chart (can be used to change other SVGs) but it affects the chart.
This does not work:
#chart_div * {
fill: unset;
}
See this fiddle: https://jsfiddle.net/g37fjyLx/1/
I realize that setting the fill for all elements * is probably not a good idea, but I'd still like to know if there is a way to reset it.
you need to use backgroundColor.fill. Please refer this https://developers.google.com/chart/interactive/docs/gallery/ganttchart#configuration-options
var options = {
height: 275,
backgroundColor: { fill: "#333" }
};
use this for your color:
var options = {
height: 275,
gantt: {
criticalPathEnabled: false, // Critical path arrows will be the same as other arrows.
arrow: {
angle: 100,
width: 5,
color: 'blue',
radius: 0
}
}
};
I have struggled the whole day to create a centered carousel with react-alice-carousel library and the results are those. Link to the library here
Basically I achieve the results showcased on the gif which were fulfilling enough for me but the fact that the focused image (the one on the left) is not centered is bugging me a lot. I have tried overwriting certain CSS properties but the results were not great (carousel item disappears if I try absolute positioning). My question is - is there a certain algorithm I can add to my logic so that the centered image would be in the middle of the carousel always? (2nd item in array of 3, 3rd item in array of 5, 4th item in an array of 7 and etc.) and if such algorithm does not exist how can I achieve to make my carousel centered-oriented.
Sandbox: https://codesandbox.io/s/happy-lichterman-knwbce?file=/src/App.js
Targeted tile (plays the role of the product in my gif) is marketed in red color
Also mostly my carousel in the project:
// Carousel helper properties / functions
const responsive = {
2000: {
items: 11,
},
1200: {
items: 5,
},
800: {
items: 3,
},
0: {
items: 1,
},
};
const handleDragStart = (e: any) => e.preventDefault();
const items = products.map((product, index) => (
<img
key={product._id}
className={selectedIndex === index ? "focusedImage" : "shownImage"}
onDragStart={handleDragStart}
style={{ height: "150px", width: "150px" }}
src={product.image}
alt='Product that is being sold on this page'
/>
));
return (
<main>
<div className='details__wrapper'>
<AliceCarousel
activeIndex={selectedIndex}
mouseTracking
responsive={responsive}
items={items}
infinite
controlsStrategy={"default"}
autoPlayStrategy='all'
autoPlayInterval={1000}
disableDotsControls
disableButtonsControls
keyboardNavigation
onSlideChanged={(e: EventObject) => {
setSelectedIndex(e.item);
setSelectedProduct(products[e.item]);
}}
/>
As seen all of the images that are seen by the user already have classes "shown" or "focused" image
And my SASS:
.alice-carousel {
&__wrapper {
& .alice-carousel__stage {
&-item {
z-index: 0 !important;
& .shownImage {
opacity: 0.5;
margin: 0 auto;
grid-area: image;
display: block;
border: 2px solid;
border-radius: 50%;
height: 20rem !important;
width: 20rem !important;
}
&.__target {
z-index: 1 !important;
.focusedImage {
opacity: 1 !important;
}
}
}
}
}
}
Mostly classes already listed in the documentation on GitHub.
I'm building "Tagging from photo" functionality.
When the user move or pinch the square on the image,
PanResponder changes the state of x-coordinate(left), y-coordinate(top), the length of square(thumbSize)
With the data, I want to show the part of square real-time
So this image below should be placed into the left of A, All All from the image above.
Here is the part of render showing the "cropped" image.
console.log(left) // 80
console.log(top) // 200
console.log(thumbSize) // 150
<Image
source={{uri: image}}
style={{height:70, width: 70, bottom: (-top), right: (-left)
}} <- style is not complete. I'm putting some example code
/>
This is continuous problem from: How to show the only part of the image.
It works but the solution doesn't meet my expectation.
It's not changing width and height ( I want to fix resize the image from 'the width of square' to '70' for each width and height)
It breaks the whole style (A, All, All things disappear)
I've been trying to solve this idea for days but couldn't find the exact way.
Update: I almost solved it but resizing matters
I changed Image to CroppedImage (new component)
<CroppedImage
source={{uri: image}}
cropTop={top}
cropLeft={left}
cropWidth={thumbSize}
cropHeight={thumbSize}
width={width(100)}
height={width(100)}
resizeMode="contain" />
Here is CroppedImage
return (
<View style={[{
overflow: 'hidden',
height: this.props.cropHeight,
width: this.props.cropWidth,
backgroundColor: 'transparent'
}, this.props.style]}>
<Image style={{
position: 'absolute',
top: this.props.cropTop * -1,
left: this.props.cropLeft * -1,
width: this.props.width,
height: this.props.height
}}
source={this.props.source}
resizeMode={this.props.resizeMode}>
{this.props.children}
</Image>
</View>
);
It seems working but it can't resize (from square width x height to 70x70).
I made a fiddle to show what calculations you have to do to correctly position and resize your tag image:
$('#image').click(function(event) {
var size_ratio = .6;
var img_src = $(this).attr('src');
var tag = $('#tag-rectangle');
var top_position = tag.height()/2 - event.offsetX*size_ratio;
var left_position = tag.width()/2 - event.offsetY*size_ratio;
$('#tag-rectangle').css({
'background-image': 'url('+img_src+')',
'background-position': top_position +'px '+ left_position + 'px',
'background-size': $(this).width()*size_ratio + 'px ' + $(this).height()*size_ratio + 'px'
});
});
#tag-rectangle {
width: 50px;
height: 50px;
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="image" src="http://fakeimg.pl/250x100/" alt="">
<div id="tag-rectangle"></div>
Well, I finally managed to create a working React Native code (never used it before, sorry if it is noobish code) doing the same as in my other answer.
Here is the code:
import React, { Component } from 'react';
import { TouchableWithoutFeedback, ImageBackground, Image, View, StyleSheet } from 'react-native';
const IMAGEURI = 'http://fakeimg.pl/300x300/';
const SIZERATIO = .6;
const IMAGEWIDTH = 300;
const IMAGEHEIGHT = 300;
const CROPIMAGEWIDTH = 100;
const CROPIMAGEHEIGHT = 100;
export default class App extends Component {
state = {
style: {
marginLeft: 0,
marginTop: 0,
},
uri: ''
};
repositionImage(event) {
this.setState({
style: {
marginLeft: CROPIMAGEWIDTH/2 - event.nativeEvent.locationX*SIZERATIO,
marginTop: CROPIMAGEHEIGHT/2 - event.nativeEvent.locationY*SIZERATIO
},
uri: IMAGEURI
});
}
render() {
return (
<View>
<TouchableWithoutFeedback onPress={(event) => this.repositionImage(event)}>
<View>
<Image
style={styles.image}
source={{ uri: IMAGEURI }}
/>
</View>
</TouchableWithoutFeedback>
<View style={styles.tag}>
<ImageBackground style={[styles.cropped,this.state.style]} source={{uri: this.state.uri }} />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
image: {
width: IMAGEWIDTH,
height: IMAGEHEIGHT,
},
tag: {
borderWidth: 1,
borderColor: '#000',
width: CROPIMAGEWIDTH,
height: CROPIMAGEHEIGHT,
overflow: 'hidden'
},
cropped: {
width: IMAGEWIDTH*SIZERATIO,
height: IMAGEHEIGHT*SIZERATIO
}
});
And here is the Snack
I really hope it helped!! Good luck!!
EDIT: Ok I will explain a bit what I'm doing here.
First, I set a State with the parameters that will change based on some event:
state = {
style: {
marginLeft: 0,
marginTop: 0,
},
uri: ''
};
Then, I make the component to get its properties from that state:
< ImageBackground style={[styles.cropped,this.state.style]} source={{uri: this.state.uri }} />
Finally, I prepare the onPress event to call a function which will update the state:
< TouchableWithoutFeedback onPress={(event) => this.repositionImage(event)}>
Here I'm feeding my function with the event object so I will be available to get the coordinates where the user pressed.
That last function takes the data from the event and updates the state. The view will automatically refresh with the new state data.
repositionImage(event) {
this.setState({
style: {
marginLeft: CROPIMAGEWIDTH/2 - event.nativeEvent.locationX*SIZERATIO,
marginTop: CROPIMAGEHEIGHT/2 - event.nativeEvent.locationY*SIZERATIO
},
uri: IMAGEURI
});
}
To position the image, I simply do a math operation:
CROPIMAGEWIDTH is the width of my tag element so to get the center I divide it by 2. Then, I substract the locationX of the event to move the image to the left so the locationX will be at the center of the tag.
That is only for positioning. To scale it just multiply the size of the image and the locationX by the same value. Note I multiplied the width and height of the image with the SIZERATIO in the cropped style
cropped: {
width: IMAGEWIDTH*SIZERATIO,
height: IMAGEHEIGHT*SIZERATIO
}
An example of this scaling stuff:
If your image has 200 width and you want to scale it to a half, you multiply it by 0.5. So if you click at the, say, pixel 180 starting for the left, the equivalent pixel for your scaled image will have to be multiplied by 0.5 too and it will be 90.
If there is something I didn't explaing clearly enough just ask me again a I will be glad to help you.
When I use two different fonts in a web page at the same font-size, they often display at different actual sizes:
This example uses two Google Fonts, Gentium and Metamorphous at the same font-size, specified as 20px.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link id="Gentium Book Basic" rel="stylesheet" type="text/css"
href="http://fonts.googleapis.com/css?family=Gentium Book Basic"
media="all">
<link id="Metamorphous" rel="stylesheet" type="text/css"
href="http://fonts.googleapis.com/css?family=Metamorphous" media="all">
</head>
<body style="font-size: 20px">
<span style="font-family: Gentium Book Basic">Test Text Length (Gentium)</span>
<br>
<span style="font-family: Metamorphous">Test Text Length (Metamorphous) </span>
</body>
</html>
A JSBin for this example can be found here.
My understanding of specifying font-size in an absolute length like px was that the font would be scaled to match that length. My expectation is that two different fonts at the same font-size would have either matching height or matching length (I understand the aspect ratios of the fonts may be different). But it doesn't appear that either is the case here. Is there some way I can make two arbitrary fonts display at either the same height or the same length without manually calculating and applying a correction?
EDIT: An example showing the descender to ascender distance for two fonts displayed at the same font size.
Clearly the two distances are not the same for these two fonts as displayed.
EDIT: An example showing letters with and without accents in the two fonts:
Again, clearly the letters are different sizes.
EDIT: Going on what is described in this article, the issue is that font-size controls the displayed size of the em value of the font. But the em value is arbitrary (it doesn't have to correspond to anything within the font, and in particular is not necessarily the height of a lower case 'm'), and does not include the ascenders and descenders, which can be any size at all (example taken from above article):
so the result is that a "100px" font can be just about any effective size whatsoever. The author of the above article computed the range of effective sizes for the Google Web Fonts at the time to be 0.618 to 3.378.
Since the font metrics (such as the em size, the capitals height, the ascender and descender values) are not exposed in CSS, there doesn't seem to be any way within CSS to make two arbitrary fonts the same effective size. For any particular font, you can use a font editor to find the font metric values and use those numbers to scale the font as required. For an arbitrary font, an option is to display some text and use the measured bounding box to determine the effective size and calculate an appropriate scaling factor.
My thanks to everyone who contributed to explaining this!
I spent a lot of time crawling through StackOverflow looking for answers for a similar situation and ended up not finding anything perfect. What I ended up doing is measuring the two fonts, and then adjusting the top margin and scale of the second font to match the first. (By using scale instead of changing the font size, it allows us to not need to re-calculate the text metrics after resizing)
I put together a couple of pens for posterity. Here's the second one, which handles the normalization of font sizes and alignments between two fonts: https://codepen.io/zacholas/pen/oNBPWga
And the first one, that only handles the measuring, is: https://codepen.io/zacholas/pen/ExZwJjx
I guess I have to paste some code in order to link to codepen, so here's all the comparison code:
HTML:
<h1>Welcome</h1>
<p>
<strong>What's all this about?</strong><br>
I've been working on the new version of the Mason image editor app, and in it, I need to compare multiple fonts to replace them with each other to have the final layout not look crappy due to spacing differences between fonts. (To allow users to customize fonts in templates and have them still look nice)
</p>
<p>The first pen focused on getting all the measurements and ratios necessary.</p>
<p>This pen encompasses the second part, which is comparison and normalization of different fonts against a default.</p>
<p>
<strong>How it works</strong><br>
First we get the metrics for the two fonts and compare their top/bottom spacing. We then normalize them to align in a purdy vertically-centered way. And then we scale down the second font's container so that it matches the size of the first.
</p>
<p>Enjoy!</p>
<p><em>- Zach</em></p>
<hr>
<h3>Demo</h3>
<p>If all of my code is working correctly, the text in the "new font adjusted" box should look all purdy and be vertically and horizontally centered.</p>
<p><strong>NOTE:</strong><em> You'll need to make a change in the dropdown before the text in the "new font adjusted" box actually gets adjusted.</em></p>
<label for="font-picker">Choose a font to swap:</label>
<select id="font-picker" disabled>
<option value=""> — Template Default — </option>
</select>
<div >
<div id="image-box" class="flex-row">
<div>
<h6>Original:</h6>
<div class="reference-box">
<div class="text-background">
<div class="text-container text-utc-lander" id="original-text">
Hxy
</div>
</div>
</div>
</div>
<div>
<h6>New font unadjusted:</h6>
<div class="reference-box">
<div class="text-background">
<div class="text-container text-utc-lander" id="unadjusted-text">
Hxy
</div>
</div>
</div>
</div>
<div>
<h6>New font adjusted:</h6>
<div id="modified" class="reference-box">
<div class="text-background">
<div class="scaler" id="adjusted-text-scaler">
<div class="text-container text-utc-lander" id="adjusted-text">
Hxy
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<hr>
<h2>Canvases used for calculating</h2>
<div id="sample-output-container">
</div>
SCSS:
#import 'https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css';
// The font size in the demo boxes
$test-font-size: 200px!default;
// $test-font-size: 100px;
body {
background: #eee;
padding: 10px;
font-family: Arial;
}
hr {
margin: 40px 0;
}
h6 {
font-size: 17px;
margin: 12px 0 5px 0;
}
//* In production, you should probably use code like this to position canvases off-screen:
// .test-canvas {
// position: fixed;
// top: -99999px;
// left: -99999px;
// display:none;
// }
.text-utc-lander { font-family: 'UTCLander-Regular'; }
.text-ar-bonnie { font-family: 'ARBONNIE'; }
.text-adam-cg { font-family: 'ADAMCGPRO'; }
.text-abolition { font-family: 'Abolition-Regular'; }
.text-avenir { font-family: 'AvenirNextLTPro-BoldItalic'; }
.text-agency { font-family: 'AgencyFB-Reg'; }
/* Testing a real life example with absolute CSS done to make a F-ed up font
like UTC lander look good, which we'll then need to modify positioning and
sizing for in order for it to look good with normal fonts */
.flex-row {
display: flex;
justify-content: space-between;
}
#image-box {
.reference-box {
background: url('https://mason-app-staging.herokuapp.com/images/sports_stadium_generic.jpg');
background-size: cover;
position: relative;
width: $test-font-size * 2;
height: $test-font-size * 1.2;
&:before, &:after {
content: '';
left: $test-font-size * .1;
right: $test-font-size * .1;
position: absolute;
height: 1px;
background: rgba(0,0,0,0.1);
z-index: 5;
}
&:before {
top: $test-font-size * 0.245;
}
&:after {
bottom: $test-font-size * 0.245;
}
.text-background {
position: absolute;
left: ($test-font-size * 0.1);
top: ($test-font-size * 0.1);
width: ($test-font-size * 1.8);
height: ($test-font-size * 1);
background:#39b510;
color: #fff;
text-transform: uppercase;
display: flex;
align-items: center;
justify-content: center;
}
.text-container {
margin-top: -10px; // Will be overwritten anyway
text-align: center;
font-size: $test-font-size;
line-height: 1;
}
}
}
#comparison-output {
background: #fff;
padding: 20px;
margin-top: 40px;
flex: 1;
}
//* Debug output from the first example
#sample-output-container {
// * {
// line-height: 1;
// }
> div {
width: 700px;
background: #CCC;
margin-bottom: 20px;
position: relative;
height: 200px;
> .text-container {
background: #fff;
position: absolute;
display: flex;
height: 150px;
left: 25px;
width: 300px;
top: 25px;
align-items: center;
justify-content: center;
> span {
background: #edc79e;
}
}
> .info-box {
font-size: 12px;
font-family: Arial;
background: #fff;
position: absolute;
width: 300px;
top: 25px;
right: 25px;
padding: 10px;
}
}
}
/*
Webfonts
- Code from here down is just base 64'd webfonts.
- All are in "normal" font weight
- Families available:
- 'ARBONNIE';
- 'ADAMCGPRO';
- 'Abolition-Regular';
- 'AgencyFB-Reg';
- 'AvenirNextLTPro-BoldItalic';
- 'UTCLander-Regular';
*/
/* ***** SKIPPING BASE-64'D FONTS FOR STACKOVERFLOW */
JS:
import FontFaceObserver from "https://cdn.skypack.dev/fontfaceobserver#2.1.0";
// var FontFaceObserver = require('fontfaceobserver');
const TYPE_DEFAULT_FONT = 'defaultFont';
const TYPE_CURRENT_FONT = 'currentFont';
// debug output canvases
const removeCalculationCanvases = false;
const allAvailableFonts = [
{ label: 'AR Bonnie', value: 'ARBONNIE' },
{ label: 'Adam CG Pro', value: 'ADAMCGPRO' },
{ label: 'Abolition Regular', value: 'Abolition-Regular' },
{ label: 'Avenir Next LT Pro Bold Italic', value: 'AvenirNextLTPro-BoldItalic' },
{ label: 'Agency FB', value: 'AgencyFB-Reg' },
{ label: 'UTC Lander', value: 'UTCLander-Regular' },
]
const INITIAL_STATE = {
[TYPE_DEFAULT_FONT]: {
label: null,
fontFamily: null,
fontSize: null,
metrics: {},
},
[TYPE_CURRENT_FONT]: {
label: null,
fontFamily: null,
fontSize: null,
metrics: {},
postAdjustmentMetrics: {}
}
}
let state = {
...INITIAL_STATE
}
const _roundToTwo = num => {
return +(Math.round(Number(num) + "e+2") + "e-2");
}
const _roundToFive = num => {
return +(Math.round(Number(num) + "e+5") + "e-5");
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const getTextMetrics = async(fontFamily, fontSize, testtext = 'Sixty Handgloves ABC') => {
//* For now we'll just keep the test text hard-coded but maybe we'll pass in the element value at some point. (However, being that the text will be editable I don't think that's wise)
testtext = 'Hxy';
const fontSizePx = fontSize.split('px')[0];
//* Generate a hash from the font name for the canvas ID
const canvasId = Math.abs(fontFamily.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0));
console.log('waiting for font to load')
var font = new FontFaceObserver(fontFamily);
await font.load();
console.log('font loaded');
//* Initialize the test canvas so that we can measure stuff
const testCanvasWidth = 400;
const testCanvasHeight = 200;
const testCanvasPadding = 10;
// const canvasDrawingTextFontSize = 1000;
const canvasDrawingTextFontSize = fontSizePx;
const testCanvas = document.createElement('canvas');
testCanvas.id = (`cvs-${canvasId}-${Math.random().toString(36).substring(7)}`);
testCanvas.className = `test-canvas ${canvasId}`;
testCanvas.width = testCanvasWidth;
testCanvas.height = testCanvasHeight;
// document.body.appendChild(testCanvas);
var testCanvasCtx = testCanvas.getContext("2d");
testCanvas.style.font = `${canvasDrawingTextFontSize}px ${fontFamily}`;
testCanvasCtx.font = [`${canvasDrawingTextFontSize}px`, fontFamily].join(' ');
testCanvasCtx.clearRect(0, 0, testCanvasWidth, testCanvasHeight);
testCanvasCtx.fontFamily = fontFamily;
testCanvasCtx.fillStyle = "#fff";
testCanvasCtx.fillRect(0,0,testCanvas.width, testCanvas.height);
testCanvasCtx.fillStyle = "#333333";
testCanvasCtx.fillText(testtext, testCanvasPadding, testCanvasHeight);
// console.log('before timeout');
// await timeout(3000);
// console.log('timeout done');
document.body.appendChild(testCanvas);
//* Get Core Measurements
var xHeight = testCanvasCtx.measureText("x").height;
var capHeight = testCanvasCtx.measureText("H").height;
// var measuredTextMetrics = testCanvasCtx.measureText("Hxy");
var measuredTextMetrics = testCanvasCtx.measureText(testtext);
//* Make the measurements usable (cast to numbers to allow for nulls)
let metrics = {};
metrics.measured = {
actualBoundingBoxAscent: _roundToFive(measuredTextMetrics.actualBoundingBoxAscent),
actualBoundingBoxDescent: _roundToFive(measuredTextMetrics.actualBoundingBoxDescent),
actualBoundingBoxLeft: _roundToFive(measuredTextMetrics.actualBoundingBoxLeft),
actualBoundingBoxRight: _roundToFive(measuredTextMetrics.actualBoundingBoxRight),
fontBoundingBoxAscent: _roundToFive(measuredTextMetrics.fontBoundingBoxAscent),
fontBoundingBoxDescent: _roundToFive(measuredTextMetrics.fontBoundingBoxDescent),
width: _roundToFive(measuredTextMetrics.width)
};
const fontSizeMultiplicand = fontSizePx / canvasDrawingTextFontSize;
const {
actualBoundingBoxAscent,
// actualBoundingBoxDescent,
// actualBoundingBoxLeft,
// actualBoundingBoxRight,
fontBoundingBoxAscent,
fontBoundingBoxDescent,
} = metrics.measured;
metrics.calculated = {
gapAboveText: _roundToFive((fontBoundingBoxAscent - actualBoundingBoxAscent) * fontSizeMultiplicand),
gapBelowText: _roundToFive(fontBoundingBoxDescent * fontSizeMultiplicand),
textHeight: _roundToFive(actualBoundingBoxAscent * fontSizeMultiplicand),
totalHeight: _roundToFive((fontBoundingBoxAscent + fontBoundingBoxDescent) * fontSizeMultiplicand),
};
const {
gapBelowText, gapAboveText, textHeight, totalHeight
} = metrics.calculated;
metrics.calculated.gapBelowTextPercent = _roundToFive(gapBelowText / totalHeight);
metrics.calculated.gapAboveTextPercent = _roundToFive(gapAboveText / totalHeight);
metrics.calculated.gapTopBottomRatio = _roundToFive(gapAboveText / gapBelowText);
metrics.calculated.textHeightPercent = _roundToFive(textHeight / totalHeight);
metrics.calculated.baselineMarginTop = gapBelowText - gapAboveText;
if(removeCalculationCanvases === true){
testCanvas.remove(); // cleanup
}
return metrics;
};
const setFontState = async(fontFamily, fontSize, fontLabel, type = TYPE_CURRENT_FONT) => {
if(fontFamily){
console.log('about to get text metrics')
const metrics = await getTextMetrics(fontFamily, fontSize);
console.log('metrics received');
state[type] = {
label: fontLabel ? fontLabel : fontFamily,
fontFamily,
fontSize,
metrics
}
}
else {
state[type] = {
...INITIAL_STATE[type]
}
}
return true;
}
const watchForFontChange = async() => {
document.addEventListener('input', async(event) => {
if (event.target.id !== 'font-picker') return; // Only run on the font change menu
let label = null;
if(
event.target.options.length &&
typeof event.target.options[event.target.selectedIndex] !== 'undefined' &&
event.target.options[event.target.selectedIndex].text
) {
label = event.target.options[event.target.selectedIndex].text;
}
// For now just grab font size from the default font state, but probably will change later
const fontFamily = event.target.value;
const fontSize = state[TYPE_DEFAULT_FONT].fontSize;
await setFontState(fontFamily, fontSize, label);
console.log('font changed', state);
//* Set the font families in the display
if(fontFamily){
document.getElementById(`unadjusted-text`).style.fontFamily = fontFamily;
document.getElementById(`adjusted-text`).style.fontFamily = fontFamily;
}
else {
document.getElementById(`unadjusted-text`).style.fontFamily = null;
document.getElementById(`adjusted-text`).style.fontFamily = null;
}
//* Calculate the adjustments for the new font compared to the baseline
// const currentFontSize = parseInt(state.currentFont.fontSize,10);
const defaultFontMetrics = state.defaultFont.metrics;
const currentFontMetrics = state.currentFont.metrics;
// const fontSizeAdjustPx = defaultFontMetrics.calculated.textHeight - currentFontMetrics.calculated.textHeight;
// const fontSizeAdjustPcnt = _roundToFive(fontSizeAdjustPx / currentFontMetrics.calculated.textHeight);
//* Apply the adjustments
// const newFontSize = currentFontSize + (currentFontSize * fontSizeAdjustPcnt);
// console.log('newFontSize', newFontSize);
const textToAdjust = document.getElementById(`adjusted-text`);
// const fontSizeStr = `${newFontSize}px`;
textToAdjust.style.marginTop = `${currentFontMetrics.calculated.baselineMarginTop}px`;
const scaler = document.getElementById('adjusted-text-scaler');
const scale = _roundToTwo(defaultFontMetrics.calculated.textHeight / currentFontMetrics.calculated.textHeight);
scaler.style.transform = `scale(${scale})`;
}, false);
}
const addFontOptionsToDropdown = () => {
const parentSelect = document.getElementById(`font-picker`);
for(let i=0; i < allAvailableFonts.length; i++){
const thisOption = allAvailableFonts[i];
if(thisOption.value){
const label = thisOption.label ? thisOption.label : thisOption.value;
const thisOptionTag = document.createElement("option");
thisOptionTag.value = thisOption.value;
const thisOptionText = document.createTextNode(label);
thisOptionTag.appendChild(thisOptionText);
parentSelect.appendChild(thisOptionTag);
}
}
}
const parseDefaultFont = async() => {
const thisText = document.getElementById(`original-text`);
// We might need to do some special stuff for uppercase vs non-uppercase text
const thisTextStyle = window.getComputedStyle(thisText);
const textTransform = thisTextStyle.getPropertyValue('text-transform');
const marginTop = thisTextStyle.getPropertyValue('margin-top');
console.log('marginTop', marginTop);
const uppercase = textTransform === 'uppercase';
const fontFamily = thisTextStyle.getPropertyValue('font-family');
const fontSize = thisTextStyle.getPropertyValue('font-size');
console.log('fontSize', fontSize);
await setFontState(fontFamily, fontSize, null, TYPE_DEFAULT_FONT);
document.getElementById(`original-text`).style.marginTop = `${state.defaultFont.metrics.calculated.baselineMarginTop}px`;
return !! fontFamily;
}
const init = async() => {
console.log(' ');
console.log(' ');
console.log(' ');
console.log('initialized.');
const defaultFont = await parseDefaultFont();
if(defaultFont){
addFontOptionsToDropdown(); // Parse JSON object into the select html tag
await watchForFontChange();
}
else {
// Handle Error -- for some reason there wasn't a font family for the default text.
}
document.getElementById('font-picker').disabled = false;
console.log('state after init done', state);
}
//* Wait for all the base 64'd fonts to load before we run it
document.addEventListener("DOMContentLoaded", (ready => {
init();
// setTimeout(function(){ init(); }, 1000);
}));
Think of the font-size not as the actual size of the individual characters themselves, but as the size of the blocks that contain each character, just like typeset letters:
The size of the blocks is defined in your CSS (using px, pts, ems, etc) but the actual size of the characters within those blocks can vary depending on the font used.
The actual, physical height of any given portion of the font depends on the user-defined DPI setting, current element font-size, and the particular font being used.
https://en.wikipedia.org/wiki/Em_(typography)#CSS
You can use the font-size-adjust property to help alter one of those fonts to scale it closer to the other: https://developer.mozilla.org/en-US/docs/Web/CSS/font-size-adjust although its support is currently limited to Firefox: http://caniuse.com/#feat=font-size-adjust
Font size is the size of the glyph from the ascender, such as the top of the letter 'h', to the descender, such as the bottom of the letter 'g'. If you set your font size to 20px, the length from the top of the letter 'h' to the bottom of the letter 'g' will be 20px. Some letters have terminals or spurs, the ends of a letter, may extend a px or two higher on some letters.
In your example, there is a px difference between the two fonts. The Metamorphous font has a mark above some letters that Gentium does not have and that is what accounts for the height difference.
You can read more here.
EDIT: See here with the "caron" above the C compared to the two Gentium letters on the right.
you should rather use something like rem then px :) as rem is a relative measure unit and px is absolute. But fonts always have a different size and imo its not possible what you want to achieve.
I've been attempting to style my Google Maps InfoWindow, but the documentation is very limited on this topic. How do you style an InfoWindow?
Google wrote some code to assist with this. Here are some examples: Example using InfoBubble, Styled markers and Info Window Custom (using OverlayView).
The code in the links above take different routes to achieve similar results. The gist of it is that it is not easy to style InfoWindows directly, and it might be easier to use the additional InfoBubble class instead of InfoWindow, or to override GOverlay. Another option would be to modify the elements of the InfoWindow using javascript (or jQuery), like later ATOzTOA suggested.
Possibly the simplest of these examples is using InfoBubble instead of InfoWindow. InfoBubble is available by importing this file (which you should host yourself): http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobubble/src/infobubble.js
InfoBubble's Github project page.
InfoBubble is very stylable, compared to InfoWindow:
infoBubble = new InfoBubble({
map: map,
content: '<div class="mylabel">The label</div>',
position: new google.maps.LatLng(-32.0, 149.0),
shadowStyle: 1,
padding: 0,
backgroundColor: 'rgb(57,57,57)',
borderRadius: 5,
arrowSize: 10,
borderWidth: 1,
borderColor: '#2c2c2c',
disableAutoPan: true,
hideCloseButton: true,
arrowPosition: 30,
backgroundClassName: 'transparent',
arrowStyle: 2
});
infoBubble.open();
You can also call it with a given map and marker to open on:
infoBubble.open(map, marker);
As another example, the Info Window Custom example extends the GOverlay class from the Google Maps API and uses this as a base for creating a more flexible info window. It first creates the class:
/* An InfoBox is like an info window, but it displays
* under the marker, opens quicker, and has flexible styling.
* #param {GLatLng} latlng Point to place bar at
* #param {Map} map The map on which to display this InfoBox.
* #param {Object} opts Passes configuration options - content,
* offsetVertical, offsetHorizontal, className, height, width
*/
function InfoBox(opts) {
google.maps.OverlayView.call(this);
this.latlng_ = opts.latlng;
this.map_ = opts.map;
this.offsetVertical_ = -195;
this.offsetHorizontal_ = 0;
this.height_ = 165;
this.width_ = 266;
var me = this;
this.boundsChangedListener_ =
google.maps.event.addListener(this.map_, "bounds_changed", function() {
return me.panMap.apply(me);
});
// Once the properties of this OverlayView are initialized, set its map so
// that we can display it. This will trigger calls to panes_changed and
// draw.
this.setMap(this.map_);
}
after which it proceeds to override GOverlay:
InfoBox.prototype = new google.maps.OverlayView();
You should then override the methods you need: createElement, draw, remove and panMap. It gets rather involved, but in theory you are just drawing a div on the map yourself now, instead of using a normal Info Window.
You can modify the whole InfoWindow using jquery alone...
var popup = new google.maps.InfoWindow({
content:'<p id="hook">Hello World!</p>'
});
Here the <p> element will act as a hook into the actual InfoWindow. Once the domready fires, the element will become active and accessible using javascript/jquery, like $('#hook').parent().parent().parent().parent().
The below code just sets a 2 pixel border around the InfoWindow.
google.maps.event.addListener(popup, 'domready', function() {
var l = $('#hook').parent().parent().parent().siblings();
for (var i = 0; i < l.length; i++) {
if($(l[i]).css('z-index') == 'auto') {
$(l[i]).css('border-radius', '16px 16px 16px 16px');
$(l[i]).css('border', '2px solid red');
}
}
});
You can do anything like setting a new CSS class or just adding a new element.
Play around with the elements to get what you need...
I used the following code to apply some external CSS:
boxText = document.createElement("html");
boxText.innerHTML = "<head><link rel='stylesheet' href='style.css'/></head><body>[some html]<body>";
infowindow.setContent(boxText);
infowindow.open(map, marker);
google.maps.event.addListener(infowindow, 'domready', function() {
// Reference to the DIV that wraps the bottom of infowindow
var iwOuter = $('.gm-style-iw');
/* Since this div is in a position prior to .gm-div style-iw.
* We use jQuery and create a iwBackground variable,
* and took advantage of the existing reference .gm-style-iw for the previous div with .prev().
*/
var iwBackground = iwOuter.prev();
// Removes background shadow DIV
iwBackground.children(':nth-child(2)').css({'display' : 'none'});
// Removes white background DIV
iwBackground.children(':nth-child(4)').css({'display' : 'none'});
// Moves the infowindow 115px to the right.
iwOuter.parent().parent().css({left: '115px'});
// Moves the shadow of the arrow 76px to the left margin.
iwBackground.children(':nth-child(1)').attr('style', function(i,s){ return s + 'left: 76px !important;'});
// Moves the arrow 76px to the left margin.
iwBackground.children(':nth-child(3)').attr('style', function(i,s){ return s + 'left: 76px !important;'});
// Changes the desired tail shadow color.
iwBackground.children(':nth-child(3)').find('div').children().css({'box-shadow': 'rgba(72, 181, 233, 0.6) 0px 1px 6px', 'z-index' : '1'});
// Reference to the div that groups the close button elements.
var iwCloseBtn = iwOuter.next();
// Apply the desired effect to the close button
iwCloseBtn.css({opacity: '1', right: '38px', top: '3px', border: '7px solid #48b5e9', 'border-radius': '13px', 'box-shadow': '0 0 5px #3990B9'});
// If the content of infowindow not exceed the set maximum height, then the gradient is removed.
if($('.iw-content').height() < 140){
$('.iw-bottom-gradient').css({display: 'none'});
}
// The API automatically applies 0.7 opacity to the button after the mouseout event. This function reverses this event to the desired value.
iwCloseBtn.mouseout(function(){
$(this).css({opacity: '1'});
});
});
//CSS put in stylesheet
.gm-style-iw {
background-color: rgb(237, 28, 36);
border: 1px solid rgba(72, 181, 233, 0.6);
border-radius: 10px;
box-shadow: 0 1px 6px rgba(178, 178, 178, 0.6);
color: rgb(255, 255, 255) !important;
font-family: gothambook;
text-align: center;
top: 15px !important;
width: 150px !important;
}
I have design google map infowindow with image & some content as per below.
map_script (Just for infowindow html reference)
for (i = 0; i < locations.length; i++) {
var latlng = new google.maps.LatLng(locations[i][1], locations[i][2]);
marker = new google.maps.Marker({
position: latlng,
map: map,
icon: "<?php echo plugins_url( 'assets/img/map-pin.png', ELEMENTOR_ES__FILE__ ); ?>"
});
var property_img = locations[i][6],
title = locations[i][0],
price = locations[i][3],
bedrooms = locations[i][4],
type = locations[i][5],
listed_on = locations[i][7],
prop_url = locations[i][8];
content = "<div class='map_info_wrapper'><a href="+prop_url+"><div class='img_wrapper'><img src="+property_img+"></div>"+
"<div class='property_content_wrap'>"+
"<div class='property_title'>"+
"<span>"+title+"</span>"+
"</div>"+
"<div class='property_price'>"+
"<span>"+price+"</span>"+
"</div>"+
"<div class='property_bed_type'>"+
"<span>"+bedrooms+"</span>"+
"<ul><li>"+type+"</li></ul>"+
"</div>"+
"<div class='property_listed_date'>"+
"<span>Listed on "+listed_on+"</span>"+
"</div>"+
"</div></a></div>";
google.maps.event.addListener(marker, 'click', (function(marker, content, i) {
return function() {
infowindow.setContent(content);
infowindow.open(map, marker);
}
})(marker, content, i));
}
Most important thing is CSS
#propertymap .gm-style-iw{
box-shadow:none;
color:#515151;
font-family: "Georgia", "Open Sans", Sans-serif;
text-align: center;
width: 100% !important;
border-radius: 0;
left: 0 !important;
top: 20px !important;
}
#propertymap .gm-style > div > div > div > div > div > div > div {
background: none!important;
}
.gm-style > div > div > div > div > div > div > div:nth-child(2) {
box-shadow: none!important;
}
#propertymap .gm-style-iw > div > div{
background: #FFF!important;
}
#propertymap .gm-style-iw a{
text-decoration: none;
}
#propertymap .gm-style-iw > div{
width: 245px !important
}
#propertymap .gm-style-iw .img_wrapper {
height: 150px;
overflow: hidden;
width: 100%;
text-align: center;
margin: 0px auto;
}
#propertymap .gm-style-iw .img_wrapper > img {
width: 100%;
height:auto;
}
#propertymap .gm-style-iw .property_content_wrap {
padding: 0px 20px;
}
#propertymap .gm-style-iw .property_title{
min-height: auto;
}
Use the InfoBox plugin from the Google Maps Utility Library. It makes styling/managing map pop-ups much easier.
Note that you'll need to make sure it loads after the google maps API:
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&callback=initMap" async defer></script>
<script src="/js/infobox_packed.js" async defer></script>
Map InfoWindow supports html and css.
I usually create html string with css class. So I can attach any style I want to.
var contentString = '<div>Any Html here</div>';
marker.infowindow = new google.maps.InfoWindow({
content: contentString,
});
Here is the ref.
You could use a css class too.
$('#hook').parent().parent().parent().siblings().addClass("class_name");
Good day!