mirror of https://github.com/zulip/zulip.git
emoji sprites: Avoid hard coding CSS percentages.
This commit changes the calculation of the background-size parameter that we use to render emojis from sprite sheets. In particular, it now makes the parameter match the sizes of our latest sprite sheets from Twitter/Google. This should fix the geometry aspect of #13959, but we also need to fix some issues with the cache being sticky. There is also some minor cleanup: - Remove obsolete -moz/-webkit CSS. - Remove needless precision in percentages. - Fix the transposed nrows/ncols names. - Add extensive commenting. Finally, we add a minor bump to the provision number. This commit should be merged in the same series as the other fix for this issue, which will probably have a major bump, and we'll need to rebase this appropriately.
This commit is contained in:
parent
621716bf30
commit
da1ce9a577
|
@ -30,9 +30,7 @@ span.emoji
|
||||||
{
|
{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-image: url('sheet-%(emojiset)s-64.png');
|
background-image: url('sheet-%(emojiset)s-64.png');
|
||||||
-webkit-background-size: 5200%%;
|
background-size: %(background_size)s;
|
||||||
-moz-background-size: 5200%%;
|
|
||||||
background-size: 5200%%;
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
|
||||||
/* Hide the text. */
|
/* Hide the text. */
|
||||||
|
@ -53,7 +51,7 @@ span.emoji
|
||||||
|
|
||||||
EMOJI_POS_INFO_TEMPLATE = """\
|
EMOJI_POS_INFO_TEMPLATE = """\
|
||||||
.emoji-%(codepoint)s {
|
.emoji-%(codepoint)s {
|
||||||
background-position: %(pos_x)s%% %(pos_y)s%%;
|
background-position: %(pos_x)s %(pos_y)s;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -83,6 +81,9 @@ def get_success_stamp() -> str:
|
||||||
sha1_hexdigest = generate_sha1sum_emoji(ZULIP_PATH)
|
sha1_hexdigest = generate_sha1sum_emoji(ZULIP_PATH)
|
||||||
return os.path.join(EMOJI_CACHE_PATH, sha1_hexdigest, 'emoji', '.success-stamp')
|
return os.path.join(EMOJI_CACHE_PATH, sha1_hexdigest, 'emoji', '.success-stamp')
|
||||||
|
|
||||||
|
def percent(f: float) -> str:
|
||||||
|
return '%0.3f%%' % (f * 100,)
|
||||||
|
|
||||||
def generate_sprite_css_files(cache_path: str,
|
def generate_sprite_css_files(cache_path: str,
|
||||||
emoji_data: List[Dict[str, Any]],
|
emoji_data: List[Dict[str, Any]],
|
||||||
emojiset: str) -> None:
|
emojiset: str) -> None:
|
||||||
|
@ -95,11 +96,33 @@ def generate_sprite_css_files(cache_path: str,
|
||||||
max_val = max(max_val, img_info[field])
|
max_val = max(max_val, img_info[field])
|
||||||
return max_val
|
return max_val
|
||||||
|
|
||||||
# Spritesheet CSS generation code. Spritesheets are squared using
|
"""
|
||||||
# padding, so we have to take only the maximum of two dimensions.
|
Spritesheets are usually NxN squares, and we have to
|
||||||
nrows = get_max_val('sheet_x', emoji_data)
|
infer N from the sheet_x/sheet_y values of emojis.
|
||||||
ncols = get_max_val('sheet_y', emoji_data)
|
"""
|
||||||
max_dim = max(nrows, ncols)
|
max_x = get_max_val('sheet_x', emoji_data)
|
||||||
|
max_y = get_max_val('sheet_y', emoji_data)
|
||||||
|
n = max(max_x, max_y) + 1
|
||||||
|
|
||||||
|
"""
|
||||||
|
Each single emoji is 64x64, with 1px gutters on every border.
|
||||||
|
We just consider the gutters to be part of the image for
|
||||||
|
simplicity reasons, so you can think of the spritesheet as
|
||||||
|
an NxN square of 66x66 pre-padded emojis. The CSS
|
||||||
|
background-size parameter below says to size the background
|
||||||
|
element as N times the size of the element that you're drawing.
|
||||||
|
|
||||||
|
Note that we use percentages here, instead of absolute
|
||||||
|
pixel values, because when we render emojis as actual elements,
|
||||||
|
their size will vary depending on which part of the UI we're
|
||||||
|
in (message emojis, emoji reactions, emoji popup, emoji
|
||||||
|
popup showcase, etc.).
|
||||||
|
|
||||||
|
(The next step is to offset the image; that will be in the
|
||||||
|
upcoming loop.)
|
||||||
|
"""
|
||||||
|
background_size = percent(n)
|
||||||
|
|
||||||
emoji_positions = ""
|
emoji_positions = ""
|
||||||
for emoji in emoji_data:
|
for emoji in emoji_data:
|
||||||
if emoji["has_img_google"]:
|
if emoji["has_img_google"]:
|
||||||
|
@ -108,16 +131,56 @@ def generate_sprite_css_files(cache_path: str,
|
||||||
# Google emoji (not just the universal ones), we need to
|
# Google emoji (not just the universal ones), we need to
|
||||||
# ensure the spritesheet is setup to correctly display
|
# ensure the spritesheet is setup to correctly display
|
||||||
# those google emoji (in case anyone used them).
|
# those google emoji (in case anyone used them).
|
||||||
|
|
||||||
|
"""
|
||||||
|
For background-position we need to use percentages.
|
||||||
|
Absolute pixel values won't work, because the size
|
||||||
|
of the background sprite image is proportional to
|
||||||
|
the size of the element we're rendering, and we render
|
||||||
|
elements in multiple sizes.
|
||||||
|
|
||||||
|
The way that CSS background-position works is linear
|
||||||
|
interpolation. When you tell CSS background-position
|
||||||
|
is "42% 37%", then in the `x` dimension it will align
|
||||||
|
the image such that 42% of the background image is to
|
||||||
|
the left of the 42% mark in the element itself.
|
||||||
|
|
||||||
|
For simplicity assume we render the emoji as 66px
|
||||||
|
(and everything will scale appropriately for other
|
||||||
|
size images as long as we use percentages).
|
||||||
|
|
||||||
|
The image size will be 66n.
|
||||||
|
The left offset of the x-th emoji (including its
|
||||||
|
padding) will be 66x. And the element's width
|
||||||
|
will be 66.
|
||||||
|
|
||||||
|
So, solve this equation for `p`, where p is
|
||||||
|
the ratio that we'll later express as a
|
||||||
|
percentage:
|
||||||
|
|
||||||
|
<image offset> = <offset of p% mark of element>
|
||||||
|
(p * 66n) = 66x + p66
|
||||||
|
p * n = x + p
|
||||||
|
p * n - p = x
|
||||||
|
p * (n - 1) = x
|
||||||
|
p = x / (n - 1)
|
||||||
|
|
||||||
|
If you ever want to change the code so that the
|
||||||
|
gutters don't show up in the element, the algebra
|
||||||
|
will get more complicated.
|
||||||
|
"""
|
||||||
|
|
||||||
emoji_positions += EMOJI_POS_INFO_TEMPLATE % {
|
emoji_positions += EMOJI_POS_INFO_TEMPLATE % {
|
||||||
'codepoint': get_emoji_code(emoji),
|
'codepoint': get_emoji_code(emoji),
|
||||||
'pos_x': (emoji["sheet_x"] * 100) / max_dim,
|
'pos_x': percent(emoji["sheet_x"] / (n - 1)),
|
||||||
'pos_y': (emoji["sheet_y"] * 100) / max_dim,
|
'pos_y': percent(emoji["sheet_y"] / (n - 1)),
|
||||||
}
|
}
|
||||||
|
|
||||||
SPRITE_CSS_PATH = os.path.join(cache_path, '%s-sprite.css' % (emojiset,))
|
SPRITE_CSS_PATH = os.path.join(cache_path, '%s-sprite.css' % (emojiset,))
|
||||||
with open(SPRITE_CSS_PATH, 'w') as f:
|
with open(SPRITE_CSS_PATH, 'w') as f:
|
||||||
f.write(SPRITE_CSS_FILE_TEMPLATE % {'emojiset': emojiset,
|
f.write(SPRITE_CSS_FILE_TEMPLATE % {'emojiset': emojiset,
|
||||||
'emoji_positions': emoji_positions,
|
'emoji_positions': emoji_positions,
|
||||||
|
'background_size': background_size,
|
||||||
})
|
})
|
||||||
|
|
||||||
def setup_emoji_farms(cache_path: str, emoji_data: List[Dict[str, Any]]) -> None:
|
def setup_emoji_farms(cache_path: str, emoji_data: List[Dict[str, Any]]) -> None:
|
||||||
|
|
|
@ -26,4 +26,4 @@ LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.org/2019/12/13/zulip-2-1-relea
|
||||||
# historical commits sharing the same major version, in which case a
|
# historical commits sharing the same major version, in which case a
|
||||||
# minor version bump suffices.
|
# minor version bump suffices.
|
||||||
|
|
||||||
PROVISION_VERSION = '73.1'
|
PROVISION_VERSION = '73.2'
|
||||||
|
|
Loading…
Reference in New Issue