ember-modal-dialog
ember-modal-dialog copied to clipboard
`transform: translate(-50%, -50%);` causing Text Blur
The modal is positioned using transform: translate(-50%, -50%);
but it causes elements in the modal to be blurred:
Element in modal with transform:
Element in modal without transform:
I'm on latest Chrome (Version 63.0.3239.132 (Official Build) (64-bit))
As a workaround, I modify the styles of ember-modal-overlay
to use flex positioning instead:
display: flex;
align-items: center;
justify-content: center;
I hope this is about centering the object into exact center of the screen. Blurring happens with the
transform: translate(-50%,-50%);
so instead doing
position: absolute;
margin:0;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
I tried injecting style in to element using javascript. (React.js)
const node = ReactDOM.findDOMNode(this);
var width = node.offsetWidth;
var height = node.offsetHeight;
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
style={
left : (windowWidth/2) - (this.state.width/2) + 'px',
right: (windowWidth/2) - (this.state.width/2) + 'px',
top: (windowHeight/2) - (this.state.height/2) + 'px',
bottom: (windowHeight/2) - (this.state.height/2) + 'px'
}
inject style into element by javascript style
Eg.
export default class Dialog extends React.Component {
constructor(props) {
super(props);
this.state = {
width: '0px',
height: '0px'
};
}
setWidthhandHeight(node){
if (this.state.width !== node.offsetWidth) {
this.setState({
width: node.offsetWidth,
height: node.offsetHeight
});
}
}
componentDidMount() {
this.setWidthhandHeight(node);
}
render() {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
return (
<dialog className={this.props.className + " dialog"}
style={{
left : (windowWidth/2) - (this.state.width/2) + 'px',
right: (windowWidth/2) - (this.state.width/2) + 'px',
top: (windowHeight/2) - (this.state.height/2) + 'px',
bottom: (windowHeight/2) - (this.state.height/2) + 'px'
}}>
{this.props.title ? (
<header><span>{this.props.title}</span></header>
)
: null
}
{this.props.children}
</dialog>
);
}
}
Its looks like the style .modal-header { border-bottom: solid 1px #bbbbbb; } is messing it up. when you overwrite this style its looks working fine for me.
In my case it seems to only happen when the body has overflow. So maybe the popup should set overflow: hidden
on html and/or body?
I just came across this issue when taking this module for a spin. Some background on why this is probably happening: https://keithclark.co.uk/articles/gpu-text-rendering-in-webkit/. So far it seems that adding position: absolute
to the modal fixes it.
Transforms are composited, meaning they don't affect layout. You can render things on subpixels outside the layout. This will cause anti-aliasing. There's not a single reason to use absolute positioning with transforms for center alignment. Use flex box like @mhluska suggests. This should be mainlined because transform: translate(-50%, -50%);
is a bug.
Edit; since it seems unclear when this bug happens; it happens whenever width or height or both are an odd number of pixels.
Transforms are composited, meaning they don't affect layout. You can render things on subpixels outside the layout. This will cause anti-aliasing. There's not a single reason to use absolute positioning with transforms for center alignment. Use flex box like @mhluska suggests. This should be mainlined because
transform: translate(-50%, -50%);
is a bug.Edit; since it seems unclear when this bug happens; it happens whenever width or height or both are an odd number of pixels.
I just tried using even dimensions for my own modal and it fixes the blurriness. Could there be a look into this bug in the future?
I had a simple menu that would slide in the view from the top, using translateY
. Even setting transform: none
on the active class didn't remove blurry text.
.menu {
position: fixed;
top: 0;
left: 0;
z-index: 205;
transform: translateY(-120%);
transition: transform 350ms;
}
.menu.active {
transform: translateY(0px);
}
What fixed the blurry text for me was adding backface-visibility: hidden;
There's not a single reason to use absolute positioning with transforms for center alignment. Use flex box like @mhluska suggests. This should be mainlined because transform: translate(-50%, -50%); is a bug.
@andrejcremoznik I've found quite a few sources that suggest using absolute positioning + transform: translate(-50%, -50%);
for centering. And, as far as I know, it's the only way to center an element of unknown size and larger than its container. Ie, it "always works" regardless of element and container sizes.
I use it all the time for centering, but I've always felt it's a bit ugly in the back of my head. But, not because I think it's a bug. For me, it's because I've thought it indicates to the browser to use the GPU to render the element. Which, to me, is overkill for simple UI things like modals, plus, I've read that people have encountered blurriness in the past. I personally haven't encountered blurriness, but I have encountered flickering.
Can you explain why you say transform: translate(-50%, -50%); is a bug?
I'm trying to prove whether it's actually "bad" or whether maybe it might actually be "good". Ie, maybe using GPU whenever possible is actually always a good idea. Maybe all websites should actually have transform: translate(0,0) on the root element just to use GPU for the entire website and have everything super fast/sharp haha I don't know!
Can you explain why you say transform: translate(-50%, -50%); is a bug?
Because it has known reproducible conditions. For example:
.wrapper {
position: relative;
width: 400px;
height: 400px;
background: black;
font-family: sans-serif;
font-size: 2em;
}
.not-blurry,
.blurry {
position: absolute;
left: 200px;
display: flex;
align-items: center;
justify-content: center;
color: black;
background: yellow;
transform: translate(-50%, -50%);
}
.not-blurry {
top: 100px;
width: 124px;
height: 124px;
}
.blurry {
top: 300px;
width: 123px;
height: 123px;
}
If you have a HiDPI screen with pixel scaling you might not notice the problem. Look at it with a 1:1 pixel to device pixel ratio. The edge of yellow square won't be sharp.
And, as far as I know, it's the only way to center an element of unknown size and larger than its container
Likely false, but it's certainly the easiest. There is nothing wrong by using translate for centering, however, you must ensure that the shape you're translating will have its endpoint on a round number of pixels, not a fraction. If you put a sharp line on .5px
, it gets antialiased.
In conclusion, if you move something by 50% its size, the size better be an even number.
I don't use Ember anymore (thankfully) and I don't remember the specifics about this bug.
test: https://jsfiddle.net/fyxqvnw1/1/
Linux/Firefox: is OK
Linux/Chromium: box is blurry, text seems fine though:
This is 1:1 px scale, no browser zoom. Top box is sharp, bottom is blurry.
@andrejcremoznik thanks for getting back!
This is an interesting nugget of insight.
I feel like the GPU is doing the right thing by "smoothing things out", but in the case of UI elements, you actually don't want the smoothing. Just me being snobbishly technical here, but I'm actually not sure if this is a technically a "bug" but rather an "undesirable consequence of a feature". Ie, I think anti-aliasing of transformed (rotated, flipped, skewed, etc) media is usually desirable for most types of media (fonts, images, etc).
But, all I want is a simple way to center things (even in the case when the content is larger than the container) without fancy things like transform/flexbox/grid... And, I don't think THAT exists.
This is the closest thing I've found, but it doesn't work for elements larger than their container, https://css-tricks.com/centering-in-the-unknown/#harder-unknown-child
PS I almost never use flexbox, but I just tested centering with it using an element larger than its container, and it worked like a charm! 👍 Maybe I'll start giving flexbox another chance!
I believe the issue may be attributed to the modal height not being an integer, potentially leading to rendering errors in the browser.