ember-modal-dialog icon indicating copy to clipboard operation
ember-modal-dialog copied to clipboard

`transform: translate(-50%, -50%);` causing Text Blur

Open mhluska opened this issue 7 years ago • 11 comments

The modal is positioned using transform: translate(-50%, -50%); but it causes elements in the modal to be blurred:

Element in modal with transform:

screenshot 2018-01-09 12 28 06

Element in modal without transform:

screenshot 2018-01-09 12 28 23

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;

mhluska avatar Jan 09 '18 20:01 mhluska

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>
        );
    }
}

sachintha88 avatar Jul 24 '18 03:07 sachintha88

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.

repl-shenoy-sukumaran avatar Dec 19 '18 12:12 repl-shenoy-sukumaran

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?

jlami avatar Mar 19 '19 14:03 jlami

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.

Bouke avatar Jul 12 '19 05:07 Bouke

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.

andrejcremoznik avatar Apr 20 '20 15:04 andrejcremoznik

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?

Frontend-io avatar May 10 '20 19:05 Frontend-io

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;

aquaductape avatar May 22 '20 01:05 aquaductape

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!

paridigm avatar Apr 16 '21 21:04 paridigm

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:

Screenshot_20210417_020523

This is 1:1 px scale, no browser zoom. Top box is sharp, bottom is blurry.

andrejcremoznik avatar Apr 17 '21 00:04 andrejcremoznik

@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!

paridigm avatar Apr 17 '21 02:04 paridigm

I believe the issue may be attributed to the modal height not being an integer, potentially leading to rendering errors in the browser.

yasuomang avatar Nov 27 '23 03:11 yasuomang