make gifs animate on hover

This commit is contained in:
MathMan05 2025-04-08 20:51:57 -05:00
parent 25817fdaba
commit 4a9a17814e
10 changed files with 127 additions and 47 deletions

View file

@ -102,7 +102,7 @@ class Direct extends Guild {
const div = document.createElement("div");
div.classList.add("flexltr", "liststyle");
user.bind(div);
div.append(user.buildpfp());
div.append(user.buildpfp(undefined, div));
const userinfos = document.createElement("div");
userinfos.classList.add("flexttb");
@ -417,7 +417,7 @@ class Group extends Channel {
const myhtml = document.createElement("span");
myhtml.classList.add("ellipsis");
myhtml.textContent = this.name;
div.appendChild(this.user.buildpfp());
div.appendChild(this.user.buildpfp(undefined, div));
div.appendChild(myhtml);
(div as any).myinfo = this;
div.onclick = (_) => {

View file

@ -1,7 +1,7 @@
import {Message} from "./message.js";
import {MarkDown} from "./markdown.js";
import {embedjson, invitejson} from "./jsontypes.js";
import {getapiurls, getBulkUsers, getInstances, Specialuser} from "./utils/utils.js";
import {createImg, getapiurls, getBulkUsers, getInstances, Specialuser} from "./utils/utils.js";
import {Guild} from "./guild.js";
import {I18n} from "./i18n.js";
import {ImagesDisplay} from "./disimg.js";
@ -171,7 +171,7 @@ class Embed {
return div;
}
generateImage() {
const img = document.createElement("img");
const img = createImg(this.json.thumbnail.proxy_url);
img.classList.add("messageimg");
img.onclick = function () {
const full = new ImagesDisplay([
@ -179,7 +179,6 @@ class Embed {
]);
full.show();
};
img.src = this.json.thumbnail.proxy_url;
if (this.json.thumbnail.width) {
let scale = 1;
const max = 96 * 3;

View file

@ -3,6 +3,7 @@ import {filejson} from "./jsontypes.js";
import {ImagesDisplay} from "./disimg.js";
import {makePlayBox, MediaPlayer} from "./media.js";
import {I18n} from "./i18n.js";
import {createImg} from "./utils/utils.js";
class File {
owner: Message | null;
id: string;
@ -47,9 +48,10 @@ class File {
this.width /= scale;
this.height /= scale;
}
if (this.content_type.startsWith("image/")) {
const div = document.createElement("div");
const img = document.createElement("img");
const img = createImg(src);
if (!fullScreen) {
img.classList.add("messageimg");
div.classList.add("messageimgdiv");
@ -66,7 +68,6 @@ class File {
full.show();
}
};
img.src = src;
div.append(img);
if (this.width && !fullScreen) {
div.style.maxWidth = this.width + "px";

View file

@ -19,6 +19,7 @@ import {User} from "./user.js";
import {I18n} from "./i18n.js";
import {Emoji} from "./emoji.js";
import {webhookMenu} from "./webhooks.js";
import {createImg} from "./utils/utils.js";
class Guild extends SnowFlake {
owner!: Localuser;
@ -745,9 +746,8 @@ class Guild extends SnowFlake {
icon = guild.icon;
}
if (icon !== null) {
const img = document.createElement("img");
const img = createImg(guild.info.cdn + "/icons/" + guild.id + "/" + icon + ".png");
img.classList.add("pfp", "servericon");
img.src = guild.info.cdn + "/icons/" + guild.id + "/" + icon + ".png";
divy.appendChild(img);
if (guild instanceof Guild) {
img.onclick = () => {

View file

@ -3,7 +3,7 @@ import {Channel} from "./channel.js";
import {Direct} from "./direct.js";
import {AVoice} from "./audio/voice.js";
import {User} from "./user.js";
import {getapiurls, getBulkUsers, SW} from "./utils/utils.js";
import {createImg, getapiurls, getBulkUsers, SW} from "./utils/utils.js";
import {getBulkInfo, setTheme, Specialuser} from "./utils/utils.js";
import {
channeljson,
@ -88,8 +88,7 @@ class Localuser {
const userInfo = document.createElement("div");
userInfo.classList.add("flexltr", "switchtable");
const pfp = document.createElement("img");
pfp.src = specialUser.pfpsrc;
const pfp = createImg(specialUser.pfpsrc);
pfp.classList.add("pfp");
userInfo.append(pfp);
@ -1244,24 +1243,26 @@ class Localuser {
content.classList.add("discovery-guild");
if (guild.banner) {
const banner = document.createElement("img");
const banner = createImg(
this.info.cdn + "/icons/" + guild.id + "/" + guild.banner + ".png?size=256",
);
banner.classList.add("banner");
banner.crossOrigin = "anonymous";
banner.src = this.info.cdn + "/icons/" + guild.id + "/" + guild.banner + ".png?size=256";
banner.alt = "";
content.appendChild(banner);
}
const nameContainer = document.createElement("div");
nameContainer.classList.add("flex");
const img = document.createElement("img");
const img = createImg(
this.info.cdn +
(guild.icon
? "/icons/" + guild.id + "/" + guild.icon + ".png?size=48"
: "/embed/avatars/3.png"),
);
img.classList.add("icon");
img.crossOrigin = "anonymous";
img.src =
this.info.cdn +
(guild.icon
? "/icons/" + guild.id + "/" + guild.icon + ".png?size=48"
: "/embed/avatars/3.png");
img.alt = "";
nameContainer.appendChild(img);
@ -1797,6 +1798,20 @@ class Localuser {
initState: !this.perminfo.user.disableColors,
},
);
const gifSettings = ["hover", "always", "never"] as const;
accessibility.addSelect(
I18n.accessibility.playGif(),
(i) => {
localStorage.setItem("gifSetting", gifSettings[i]);
},
gifSettings.map((_) => I18n.accessibility.gifSettings[_]()),
{
defaultIndex:
((gifSettings as readonly string[]).indexOf(
localStorage.getItem("gifSetting") as string,
) + 1 || 1) - 1,
},
);
}
{
const connections = settings.addButton(I18n.getTranslation("localuser.connections"));
@ -1891,15 +1906,14 @@ class Localuser {
const container = document.createElement("div");
if (application.cover_image || application.icon) {
const cover = document.createElement("img");
cover.crossOrigin = "anonymous";
cover.src =
const cover = createImg(
this.info.cdn +
"/app-icons/" +
application.id +
"/" +
(application.cover_image || application.icon) +
".png?size=256";
"/app-icons/" +
application.id +
"/" +
(application.cover_image || application.icon) +
".png?size=256",
);
cover.alt = "";
cover.loading = "lazy";
container.appendChild(cover);

View file

@ -574,7 +574,7 @@ class Message extends SnowFlake {
this.message_reference ||
!messageTypes.has(premessage.type);
if (combine) {
const pfp = this.author.buildpfp();
const pfp = this.author.buildpfp(undefined, div);
this.author.bind(pfp, this.guild, false);
pfpRow.appendChild(pfp);
}

View file

@ -11,6 +11,7 @@ import {I18n} from "./i18n.js";
import {Direct} from "./direct.js";
import {Hover} from "./hover.js";
import {Dialog} from "./settings.js";
import {createImg} from "./utils/utils.js";
class User extends SnowFlake {
owner: Localuser;
@ -456,10 +457,9 @@ class User extends SnowFlake {
}
}
buildpfp(guild: Guild | void | Member | null): HTMLImageElement {
const pfp = document.createElement("img");
buildpfp(guild: Guild | void | Member | null, hoverElm: void | HTMLElement): HTMLImageElement {
const pfp = createImg(this.getpfpsrc(), undefined, hoverElm);
pfp.loading = "lazy";
pfp.src = this.getpfpsrc();
pfp.classList.add("pfp");
pfp.classList.add("userid:" + this.id);
if (guild) {
@ -467,9 +467,9 @@ class User extends SnowFlake {
if (guild instanceof Guild) {
const memb = await Member.resolveMember(this, guild);
if (!memb) return;
pfp.src = memb.getpfpsrc();
pfp.setSrcs(memb.getpfpsrc());
} else {
pfp.src = guild.getpfpsrc();
pfp.setSrcs(guild.getpfpsrc());
}
})();
}
@ -479,7 +479,7 @@ class User extends SnowFlake {
async buildstatuspfp(guild: Guild | void | Member | null): Promise<HTMLDivElement> {
const div = document.createElement("div");
div.classList.add("pfpDiv");
const pfp = this.buildpfp(guild);
const pfp = this.buildpfp(guild, div);
div.append(pfp);
const status = document.createElement("div");
status.classList.add("statusDiv");
@ -560,9 +560,10 @@ class User extends SnowFlake {
changepfp(update: string | null): void {
this.avatar = update;
this.hypotheticalpfp = false;
const src = this.getpfpsrc();
Array.from(document.getElementsByClassName("userid:" + this.id)).forEach((element) => {
(element as HTMLImageElement).src = src;
//const src = this.getpfpsrc();
Array.from(document.getElementsByClassName("userid:" + this.id)).forEach((_element) => {
//(element as HTMLImageElement).src = src;
//FIXME
});
}
@ -719,12 +720,14 @@ class User extends SnowFlake {
for (const badgejson of badges) {
const badge = document.createElement(badgejson.link ? "a" : "div");
badge.classList.add("badge");
const img = document.createElement("img");
let src: string;
if (URL.canParse(badgejson.icon)) {
img.src = badgejson.icon;
src = badgejson.icon;
} else {
img.src = this.info.cdn + "/badge-icons/" + badgejson.icon + ".png";
src = this.info.cdn + "/badge-icons/" + badgejson.icon + ".png";
}
const img = createImg(src, undefined, badgediv);
badge.append(img);
let hovertxt: string;
if (badgejson.translate) {
@ -810,11 +813,11 @@ class User extends SnowFlake {
return div;
}
getBanner(guild: Guild | null | Member): HTMLImageElement {
const banner = document.createElement("img");
const banner = createImg(undefined);
const bsrc = this.getBannerUrl();
if (bsrc) {
banner.src = bsrc;
banner.setSrcs(bsrc);
banner.classList.add("banner");
}
@ -822,7 +825,7 @@ class User extends SnowFlake {
if (guild instanceof Member) {
const bsrc = guild.getBannerUrl();
if (bsrc) {
banner.src = bsrc;
banner.setSrcs(bsrc);
banner.classList.add("banner");
}
} else {
@ -830,7 +833,7 @@ class User extends SnowFlake {
if (!memb) return;
const bsrc = memb.getBannerUrl();
if (bsrc) {
banner.src = bsrc;
banner.setSrcs(bsrc);
banner.classList.add("banner");
}
});

View file

@ -583,6 +583,63 @@ export async function getapiurls(str: string): Promise<
}
return false;
}
function isAnimated(src: string) {
return src.endsWith(".apng") || src.endsWith(".gif");
}
const staticImgMap = new Map<string, string | Promise<string>>();
export function createImg(
src: string | undefined,
staticsrc: string | void,
elm: HTMLElement | void,
) {
const settings =
localStorage.getItem("gifSetting") || ("hover" as "hover") || "always" || "never";
const img = document.createElement("img");
elm ||= img;
img.crossOrigin = "anonymous";
img.onload = async () => {
if (settings === "always") return;
if (!src) return;
if (isAnimated(src) && !staticsrc) {
let s = staticImgMap.get(src);
if (s) {
staticsrc = await s;
} else {
staticImgMap.set(
src,
new Promise(async (res) => {
const c = new OffscreenCanvas(img.naturalWidth, img.naturalHeight);
const ctx = c.getContext("2d");
if (!ctx) return;
ctx.drawImage(img, 0, 0);
const blob = await c.convertToBlob();
res(URL.createObjectURL(blob));
}),
);
staticsrc = (await staticImgMap.get(src)) as string;
}
img.src = staticsrc;
}
};
elm.onmouseover = () => {
if (settings === "never") return;
if (img.src !== src && src) {
img.src = src;
}
};
elm.onmouseleave = () => {
if (staticsrc && settings !== "always") {
img.src = staticsrc;
}
};
img.src = settings !== "always" ? staticsrc || src || "" : src || "";
return Object.assign(img, {
setSrcs: (nsrc: string, nstaticsrc: string | void) => {
src = nsrc;
staticsrc = nstaticsrc;
},
});
}
/**
*
* This function takes in a string and checks if the string is a valid instance

View file

@ -155,7 +155,7 @@ async function webhookMenu(
const nameBox = document.createElement("div");
nameBox.classList.add("flexttb");
nameBox.append(name);
const pfp = user.buildpfp();
const pfp = user.buildpfp(undefined, div);
div.append(pfp, nameBox);
form.addHTMLArea(div);

View file

@ -147,7 +147,13 @@
"spoiler": "Spoiler",
"accessibility": {
"name": "Accessibility",
"roleColors": "Disable role colors"
"roleColors": "Disable role colors",
"playGif": "Play gifs:",
"gifSettings": {
"hover": "Hover",
"always": "Always",
"never": "Never"
}
},
"searchGifs": "Search Tenor",
"channel": {