ЧАСТЬ 1. Что заставляет изменение размера выглядеть хорошо или плохо?
В вопросах StackOverflow столько вопросов, связанных с плавным изменением размеров, так много неопределенности и неясности, что нам нужно создать общий словарь, чтобы помочь людям сделать свои ответы более понятными.
Это то, что мы будем делать в этом разделе.
Для простоты мы собираемся объяснить проблемы плавного изменения размера только в горизонтальном измерении, но все здесь относится только к вертикальному изменению размерато же самое.
Ниже мы будем ссылаться на
"не клиентскую область" окна: часть окна, которой управляет Windows, включая строку заголовка вверхняя и оконная границы вокруг всех краев, а также
«клиентская область»: основная часть окна, за которую вы отвечаете
Предположим,у вас есть приложение с:
- кнопкой или надписью L, которая должна оставаться заподлицо влево
- кнопкой или надписью R, которая должна оставаться заподлицо справа
независимо от того, как изменяется размер окна.
Ваше приложение может рисовать сам L / R (например, используя GDI / OpenGL / DirectX внутри одного окна) или L / R может быть неким элементом управления Microsoft (который будет иметь свой собственный HWND, отдельный от вашего главного окна HWND);не имеет значения.
Вот упрощенное представление клиентской области окна вашего приложения.Как вы можете видеть, у нас есть LLL шириной в три столбца в дальнем левом углу клиентской области и RRR шириной в три столбца в крайнем правом углу клиентской области, с различным другим содержимым клиентской области, представленным знаком «-» вмежду (пожалуйста, не обращайте внимания на серый фон, который StackOverflow настаивает на добавлении; L и R находятся на крайнем левом и правом краях вашей клиентской области):
LLL-----------RRR
Теперь представьте, что вы захватили левую или правую границуи перетащите его, чтобы сделать окно больше или меньше.
1a.Простой пример: рисование вовремя
Представьте, что ваше приложение очень быстро рисует, так что оно всегда может реагировать на действие перетаскивания пользователя в течение 1 миллисекунды, а ОС позволяет вашему приложению рисовать это быстро, не пытаясь что-либо нарисовать.еще на экране, чтобы «помочь» вам.
Когда вы перетаскиваете границу приложения, пользователь видит на экране следующее (каждая строка этих цифр представляет один момент времени):
Перетаскивание правой границы вправо (увеличение ширины):
(Figure 1a-1)
LLL-----------RRR (initially, when you click the mouse)
LLL------------RRR (as you drag the mouse)
LLL-------------RRR (as you drag the mouse)
LLL--------------RRR (when you release the mouse)
Перетаскивание правой границы влево (ширина сжатия):
(Figure 1a-2)
LLL-----------RRR
LLL----------RRR
LLL---------RRR
LLL--------RRR
Перетаскивание левой границы влево (увеличение ширины):
(Figure 1a-3)
LLL-----------RRR
LLL------------RRR
LLL-------------RRR
LLL--------------RRR
Перетаскивание левой границы вправо (ширина сжатия):
(Figure 1a-4)
LLL-----------RRR
LLL----------RRR
LLL---------RRR
LLL--------RRR
Все это выглядит хорошо и плавно:
- При настройке правой границы R кажется движущимся с постояннойскорость в одном направлении и L остается на месте, как и должно быть.
- При регулировке левой границы, L, кажется, движется с постоянной скоростью в одном направлении, а R остается неподвижным, как и должно.
Пока все хорошо.
1b.Трудный случай: рисование отстает
Теперь представьте, что ваше приложение слишком медленно рисует, так что приложение не может справиться с вами, когда вы перетаскиваете мышью.Да, в конце концов, ваш рисунок наверстает упущенное, но мы говорим о том, что происходит в то время, когда вы перетаскиваете мышь рукой.Очевидно, что компьютер не может протянуть руку и схватить вас за руку, чтобы замедлить движение мыши, поэтому ключевыми вопросами являются:
- что должно отображаться на экране в этот период и
- кто решает, что показывать?
Например, при перетаскивании правой границы вправо (увеличение ширины):
(Figure 1b-1)
LLL-----------RRR
?????????????????? (what should show here?)
??????????????????? (what should show here?)
LLL--------------RRR (app catches up)
В качестве другого примера, при перетаскивании левой границы влево (уменьшение ширины):
(Figure 1b-2)
LLL-----------RRR
???????????????? (what should show here?)
??????????????? (what should show here?)
LLL--------RRR (app catches up)
Это ключевые вопросы, которые определяют, выглядит ли движение гладким, и они являются ключевымивопросы, вокруг которых вращается весь этот вопрос StackOverflow.
Разные версии Windows предоставляют разные ответы на эти вопросы в разных контекстах, а это означает, что решение для более плавного изменения размера зависит от того, в какой ситуации вы находитесь.
1c.Временные решения в ожидании рисования приложения
Существует несколько вариантов действий в период после того, как пользователь начал перетаскивать мышь, чтобы изменить размер окна, но до того, как ваше приложение догнало его, нарисовав окно.в новом размере.
1c1.Ничего не делать
Экран может оставаться в точности таким, какой он есть, пока приложение не догонит (ни пиксели клиента, ни даже граница окна в области, не являющейся клиентом, не изменятся):
Пример при перетаскиванииправая граница вправо (увеличение ширины):
(Figure 1c1-1)
LLL-----------RRR
LLL-----------RRR
LLL-----------RRR
LLL--------------RRR (app catches up)
Пример перетаскивания левой границы влево (уменьшение ширины):
(Figure 1c1-2)
LLL-----------RRR
LLL-----------RRR
LLL-----------RRR
LLL--------RRR (app catches up)
Очевидным недостатком этого метода является то, чтов течение рассматриваемого периода приложение «зависло» и, по-видимому, не реагирует на движения мыши, потому что ни R, ни «-», ни «L», ни граница окна не движутся.
Microsoft часто считают, что Windows не отвечает на запросы операционной системы (и иногда это их ошибка, а иногда и ошибка разработчика приложения), поэтому с тех пор, как Microsoft представила live-resize (Windows XP?), Microsoft никогда не использоваласам метод "ничего не делать".
Метод "ничего не делать" раздражает пользователя и выглядит непрофессионально, но оказывается (очень неочевидно), что это не всегда худший выбор.Читайте дальше ...
1c2.Масштабирование содержимого
Другая возможность заключается в том, что Windows всегда может заставить границу окна следовать вашим движениям мыши мгновенно (поскольку сама Windows обладает достаточной вычислительной мощностью, чтобы хотя бы своевременно рисовать не-клиентскую область), и в то время как онаждет вашего приложения, Windows может взять старые пиксели клиентской области и масштабировать эти пиксели вверх или вниз так же, как при увеличении / увеличении изображения, чтобы они «помещались» в меньшем или большем пространстве.
Этот метод, как правило, хуже, чем любой другой метод, потому что он приведет к размытому изображению вашего исходного контента, которое, вероятно, будет непропорциональным.Так что никто не должен делать это в любом случае.За исключением случаев, как мы увидим в ЧАСТЬ 2 , иногда Microsoft делает.
1c3.При увеличении залейте фоновым цветом
Другой способ, который может работать при увеличении окна, заключается в следующем: Windows всегда может заставить границу окна мгновенно следовать вашим движениям мыши, и Windows может заполнить новые пиксели текущего- большая клиентская область с некоторым временным цветом фона B:
Например, при перетаскивании правой границы вправо (увеличение ширины):
(Figure 1c3-1)
LLL-----------RRR
LLL-----------RRRB
LLL-----------RRRBB
LLL--------------RRR (app catches up)
Этот метод имеет то преимущество, что во времярассматриваемый период, по крайней мере, ваша граница окна движется , поэтому приложение чувствует себя отзывчивым.
Еще одна приятная особенность заключается в том, что во время перетаскивания L остается неподвижным, как и должно быть.
Немного странно, что новое пространство, которое вы создаете при перетаскивании, заполняется каким-то случайнымцвета, и еще более странно, что R на самом деле не перемещается до позднего времени (обратите внимание, что R дергается влево на 3 столбца в последний момент), но по крайней мере R перемещается только в правильном направлении.Это частичное улучшение.
Огромный и важный вопрос: какого цвета должен быть новый цвет фона B?Если B окажется черным, а ваше приложение будет иметь в основном белый фон, или наоборот, это будет намного страшнее, чем если B соответствует цвету фона вашего существующего контента.Как мы увидим в ЧАСТЬ 2 , Windows развернула несколько различных стратегий для улучшения выбора B.
Теперь рассмотрим ту же идею, но вместо этого применим ее к случаю, когда мы перетаскиваемлевая граница слева (увеличение ширины).
Логично было бы заполнить новый цвет фона в левой части окна:
(Figure 1c3-2)
LLL-----------RRR
BLLL-----------RRR
BBLLL-----------RRR
LLL--------------RRR (app catches up)
Это было бы логично, поскольку R останется на месте, как и должно быть.У L будет та же странность, которую мы описали вместе с рисунком 1c3-1 выше (L будет стоять неподвижно, а затем в последний момент внезапно дернет 3 столбца влево), но по крайней мере L будет двигаться только в правильном направлении.
Однако - и это действительно станет шоком - в нескольких важных случаях, с которыми вам приходится иметь дело, Windows не делает логическую вещь.
Вместо этого Windows иногда заполняет фоновые пиксели B справа, даже если вы перетаскиваете левую границу окна:
(Figure 1c3-3)
LLL-----------RRR
LLL-----------RRRB
LLL-----------RRRBB
LLL--------------RRR (app catches up)
Да, это безумие.
Учтитекак это выглядит для пользователя:
L, кажется, очень плавно движется с постоянной скоростью в одном направлении, так что это на самом деле хорошо, но
Просто посмотрите на то, что делает R:
RRR
RRR
RRR
RRR (app catches up)
- R сначала перемещается влево на два столбца, что он должен не делать: R должен оставаться на одном уровне-право всегда
- R, затем снова фиксирует вправо .Святое дерьмо!
Это выглядит ужасно, ужасно, ужасно, отвратительно, ... даже нет слов, чтобы описать, как плохо это выглядит.
Человеческий глаз чрезвычайно чувствителен к движению, даже к движению, которое происходит всего за несколько периодов времени.Наш глаз мгновенно замечает это странное движение вперед и назад R, и мы сразу же узнаем, что что-то серьезно не так.
Так что здесь вы можете начать понимать, почему некоторые из этих уродливых проблем с изменением размера происходят толькокогда вы перетаскиваете левую (или верхнюю) границу, а не правую (или нижнюю) границу.
В действительности оба случая (рис. 1c3-2 и рис. 1c3-3) делают что-то странное.На рисунке 1c3-2 мы временно добавляем фоновые пиксели B, которые там не принадлежат.Но это странное поведение гораздо менее заметно, чем движение вперед-назад на рисунке 1c3-3.
Это движение вперед-назад равно джиттеру / мерцанию / прыжку, что тако многих вопросах StackOverflow.
Таким образом, любое решение проблемы плавного изменения размера должно:
, по крайней мере, препятствовать тому, чтобы элементы в вашей клиентской области появлялись в одномзатем в обратном направлении.
в идеале также избегайте необходимости добавлять фоновые пиксели B, если возможно
1c4.При сжатии отрежьте несколько пикселей
Раздел 1c3 посвящен расширению окна.Если мы посмотрим на сжатие окна, то увидим, что существует точно такой же набор случаев.
Техника, которая может работать при сжатии окна, следующая: Windows всегда может заставить границу окна следовать вашим движениям мыши.мгновенно, и Windows может просто отрубить (обрезать) некоторые пиксели вашей теперь меньшей клиентской области.
Например, при перетаскивании правой границы влево (уменьшение ширины):
(Figure 1c4-1)
LLL-----------RRR
LLL-----------RR
LLL-----------R
LLL--------RRR (app catches up)
С помощью этой техники L остается на месте, как и должно быть, но справа происходит странная вещь: R, который должен оставаться вровень-вправо, независимо от размера окна, похоже, что его правый край постепенно срезается с помощьюправый край клиентской области, пока не исчезнет R, а затем внезапно R снова появится в правильном положении, когда приложение догонит.Это очень странно, но имейте в виду, что ни в одной точке R не движется вправо.Левый край R, кажется, остается неподвижным до последнего момента, когда все R отскакивают назад на 3 столбца влево.Итак, как мы видели на рисунке 1c3-1, R. только перемещается в правильном направлении.
Теперь рассмотрим, что происходит, когда мы перетаскиваем левую границу вправо (ширина сжатия).
логично было бы стричь пиксели слева от клиентской области:
(Figure 1c4-2)
LLL-----------RRR
LL-----------RRR
L-----------RRR
LLL--------RRR (app catches up)
Это будет иметь те же странные свойства, что и на рисунке 1c4-1, только с измененными ролями влево и вправо. Казалось бы, что L постепенно сбривается от левого края L, но правый край L будет оставаться неподвижным до тех пор, пока в последний момент L не появится, чтобы прыгнуть вправо. Таким образом, L движется только в правильном направлении, хотя и резко.
Но - да, будьте готовы снова к полному шоку - в нескольких важных случаях, с которыми вам приходится иметь дело, Windows не делает ничего логичного.
Вместо этого Windows иногда обрезает пиксели справа, даже если вы перетаскиваете левую границу окна:
(Figure 1c4-3)
LLL-----------RRR
LLL-----------RR
LLL-----------R
LLL--------RRR (app catches up)
Посмотрите, как это выглядит для пользователя:
L, кажется, движется очень плавно с постоянной скоростью в одном направлении, так что это действительно хорошо, но
Просто посмотрите, что делает R:
RRR
RR
R
RRR (app catches up)
- R сначала скользит вправо на два столбца. Левый край R, кажется, движется вправо вместе с остальной частью R.
- R затем снова щелкает влево снова.
Как вы теперь должны знать после прочтения раздела 1c3, это движение назад и вперед выглядит абсолютно ужасно и намного хуже, чем, по общему признанию, странное поведение на рис. 1c4-1 и рис. 1c4-2.
1c5. Подождите немного, затем попробуйте один из вышеперечисленных
Пока что мы представили отдельные идеи о том, что делать, когда пользователь начал перетаскивать границы окна, но приложение еще не перерисовалось.
Эти методы действительно можно комбинировать.
На мгновение попробуйте подумать об этой проблеме с точки зрения Microsoft. В тот момент, когда пользователь начинает перетаскивать мышь, чтобы изменить размер окна, у Microsoft нет возможности заранее узнать, сколько времени займет рисование вашего приложения. Поэтому Microsoft должна найти баланс:
если ваше приложение будет реагировать быстро, то любые изменения, которые Microsoft вносит в экран, заставят ваше приложение выглядеть хуже, чем если бы Microsoft просто позволяла вам рисовать реальный контент (помните, все вышеперечисленные уловки странные в разной степени и сделает ваш контент странным, поэтому лучше не использовать эти приемы).
но если Microsoft будет ждать, пока вы начнете рисовать слишком долго, ваше приложение (и Windows по расширению) будет выглядеть зависшим и не отвечающим, как мы объяснили в Разделе 1c1. Это делает Microsoft потерять лицо, даже если это ваша вина.
Итак, еще один вариант - сначала приостановить изменения на экране и дать приложению определенное время для рисования, а если приложение не уложится в крайний срок, тогда воспользуйтесь одним из методов, указанных выше, чтобы временно «заполнить». разрыв. "
Это звучит для вас ужасно и смешно? Угадай, что? Это то, что делает Windows, по крайней мере, двумя разными способами одновременно с двумя разными сроками. ЧАСТЬ 2 погрузится в эти дела ...