Размер виджета `Image` для мониторов с плотным пикселем (" Retina ") в приложении Vaadin 14 - PullRequest
1 голос
/ 27 октября 2019

Я знаю, как отобразить изображение JPEG на экране в приложении Vaadin 14.1.

В частности, 3 строки:

imageReader.setInput( imageInputStream ); // Render the image from the data provided by the `ImageInputStream`.
imageWidget.setWidth( imageReader.getWidth( 0 ) + "px" ); // Get the pixel size of the rendered image.
imageWidget.setHeight( imageReader.getHeight( 0 ) + "px" );

… этого:

private Image createImageWidget ( String mimeType , String fileName , InputStream inputStreamOfImageData )
{
    if ( mimeType.startsWith( "image" ) )
    {
        Image imageWidget = new Image();
        try (
                inputStreamOfImageData ;
                // Auto-close.
        )
        {
            // Get the octets of the image data.
            final byte[] bytes = IOUtils.toByteArray( inputStreamOfImageData );

            // Make the image widget for Vaadin.
            imageWidget.getElement().setAttribute( "src" , new StreamResource( fileName , ( ) -> new ByteArrayInputStream( bytes ) ) );
            imageWidget.setAlt( "File name: " + fileName );

            // Size the image for display in the HTML/CSS for faster rendering without the page layout jumping around in front of user.
            try (
                    ImageInputStream imageInputStream = ImageIO.createImageInputStream( new ByteArrayInputStream( bytes ) ) ;
                    // Auto-close.
            )
            {
                // Apparently, image readers (parsers/decoders) are detected at runtime and loaded via the "Java Service Provider Interface (SPI)" facility.
                final Iterator < ImageReader > imageReaders = ImageIO.getImageReadersByMIMEType( UploadView.MIME_TYPE_JPEG );  // OR, for multiple image types, call `ImageIO.getImageReaders( in )`.
                if ( imageReaders.hasNext() )
                {
                    ImageReader imageReader = imageReaders.next();
                    try
                    {
                        imageReader.setInput( imageInputStream ); // Render the image from the data provided by the `ImageInputStream`.
                        imageWidget.setWidth( imageReader.getWidth( 0 ) + "px" ); // Get the pixel size of the rendered image.
                        imageWidget.setHeight( imageReader.getHeight( 0 ) + "px" );
                    }
                    finally
                    {
                        imageReader.dispose();
                    }
                } else
                {
                    throw new IllegalStateException( "Failed to find any image readers for JPEG. " + "Message # e91ce8e4-0bd3-424d-8f7c-b3f51c7ef827." );
                }
            }
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        return imageWidget;
    }
    // TODO: Log error. Should not reach this point.
    System.out.println( "BASIL - ERROR - Ended up with a null `imageWidget`. " + "Message # 415bd917-67e2-49c3-b39a-164b0f900a3a." );
    return null;
}

Я понимаю, что в этих 3 строках мы информируем веб-страницуширина и высота изображения вместо того, чтобы позволить веб-странице определять размер изображений при их загрузке. Это ускоряет верстку и позволяет избежать раздражения, когда веб-страница прыгает перед пользователем.

➥ У меня такой вопрос: как насчет современных мониторов с высокой плотностью пикселей, Retina отображается так, как их называет Apple?

Как получить изображение для эффективного отображениявсе его пиксели, но занимают наименьшее пространство на экране?

Если монитор имеет старомодную низкую плотность пикселей, изображение должно отображаться с большим количеством сантиметров физического пространства экрана, чем на дисплее Retina с высокой плотностью, где изображение может занимать меньше сантиметров экрана, при этом все же технически отображая всепиксели изображения.

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

При быстром поиске в Интернете не было найдено ни одной встроенной функции браузера для предоставления различных размеров элементу <img> в зависимости от собственной плотности пикселей. У нас есть свойство window.devicePixelRatio, которое можно прочитать с помощью JavaScript. См. Руководство от Mozilla .

. Вы можете использовать это вместе с executeJs двумя различными способами - либо для динамической установки размера каждого элемента, либо для извлечения значения на сервер, а затемвыполните вычисления на сервере для каждого Image компонента.

Первый вариант будет выглядеть примерно так:

imageWidget.getElement().executeJs("this.style.width = ($0 / window.devicePixelRatio) + 'px';" +
    "this.style.height = ($1 / window.devicePixelRatio) + 'px';",
  imageWidth, imageHeight);

Второй вариант для извлечения отношения и его использования будет выглядеть так:this:

ui.getPage().executeJs("return window.devicePixelRatio;")
  .then(Double.class, ratio -> this.devicePixelRatio = ratio.doubleValue());

// Later, in some other location
imageWidget.setWidth((imageWidth / this.devicePixelRatio) + "px");
imageWidget.setHeight((imageHeight / this.devicePixelRatio) + "px");

EDIT: вариант второго подхода, который использует retrieveExtendedClientDetails вместо executeJs:

ui.getPage().retrieveExtendedClientDetails(details -> this.devicePixelRatio = details.getDevicePixelRatio());

// Later, in some other location
imageWidget.setWidth((imageWidth / this.devicePixelRatio) + "px");
imageWidget.setHeight((imageHeight / this.devicePixelRatio) + "px");
1 голос
/ 27 октября 2019

В Vaadin 14 компонент Image является просто представлением тега HTML img. Это должно означать, что единицы измерения размера как em или относительные проценты должны быть применимы. Они должны работать лучше, если вы не хотите делать предположения о размере экрана. Общее эмпирическое правило при создании адаптивных дизайнов - избегать размеров в пикселях и использовать вместо них, например, em или проценты.

Но если вы хотите по каким-то причинам самостоятельно выполнять вычисления относительного размера, вы можете получить размер браузераокно таким образом:

    getUI().ifPresent(ui -> ui.getPage().retrieveExtendedClientDetails(receiver -> {
        int bodyWidth = receiver.getBodyClientWidth();
        System.out.println("Width "+bodyWidth);
    }));
...