rudeshark.net/packages/client/src/components/global/MkAvatar.vue
2023-04-07 17:01:42 -07:00

200 lines
3.6 KiB
Vue

<template>
<span
v-if="disableLink"
v-user-preview="disablePreview ? undefined : user.id"
class="eiwwqkts _noSelect"
:class="{ cat: user.isCat, square: $store.state.squareAvatars }"
:style="{ color }"
:title="acct(user)"
@click="onClick"
>
<img class="inner" :src="url" decoding="async" />
<MkUserOnlineIndicator
v-if="showIndicator && user.instance == null"
class="indicator"
:user="user"
/>
</span>
<MkA
v-else
v-user-preview="disablePreview ? undefined : user.id"
class="eiwwqkts _noSelect"
:class="{ cat: user.isCat, square: $store.state.squareAvatars }"
:style="{ color }"
:to="userPage(user)"
:title="acct(user)"
:target="target"
@click.stop
>
<img class="inner" :src="url" decoding="async" />
<MkUserOnlineIndicator
v-if="showIndicator && user.instance == null"
class="indicator"
:user="user"
/>
</MkA>
</template>
<script lang="ts" setup>
import { onMounted, watch } from "vue";
import * as misskey from "calckey-js";
import { getStaticImageUrl } from "@/scripts/get-static-image-url";
import { extractAvgColorFromBlurhash } from "@/scripts/extract-avg-color-from-blurhash";
import { acct, userPage } from "@/filters/user";
import MkUserOnlineIndicator from "@/components/MkUserOnlineIndicator.vue";
import { defaultStore } from "@/store";
const props = withDefaults(
defineProps<{
user: misskey.entities.User;
target?: string | null;
disableLink?: boolean;
disablePreview?: boolean;
showIndicator?: boolean;
}>(),
{
target: null,
disableLink: false,
disablePreview: false,
showIndicator: false,
}
);
const emit = defineEmits<{
(ev: "click", v: MouseEvent): void;
}>();
const url = $computed(() =>
defaultStore.state.disableShowingAnimatedImages
? getStaticImageUrl(props.user.avatarUrl)
: props.user.avatarUrl
);
function onClick(ev: MouseEvent) {
emit("click", ev);
}
let color = $ref();
watch(
() => props.user.avatarBlurhash,
() => {
color = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
},
{
immediate: true,
}
);
</script>
<style lang="scss" scoped>
@keyframes earwiggleleft {
from {
transform: rotate(37.6deg) skew(30deg);
}
25% {
transform: rotate(10deg) skew(30deg);
}
50% {
transform: rotate(20deg) skew(30deg);
}
75% {
transform: rotate(0deg) skew(30deg);
}
to {
transform: rotate(37.6deg) skew(30deg);
}
}
@keyframes earwiggleright {
from {
transform: rotate(-37.6deg) skew(-30deg);
}
30% {
transform: rotate(-10deg) skew(-30deg);
}
55% {
transform: rotate(-20deg) skew(-30deg);
}
75% {
transform: rotate(0deg) skew(-30deg);
}
to {
transform: rotate(-37.6deg) skew(-30deg);
}
}
.eiwwqkts {
position: relative;
display: inline-block;
vertical-align: bottom;
flex-shrink: 0;
border-radius: 100%;
line-height: 16px;
> .inner {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: 0;
border-radius: 100%;
z-index: 1;
overflow: hidden;
object-fit: cover;
width: 100%;
height: 100%;
}
> .indicator {
position: absolute;
z-index: 1;
bottom: 0;
left: 0;
width: 18%;
height: 18%;
}
&.square {
border-radius: 20%;
> .inner {
border-radius: 20%;
}
}
&.cat {
&:before,
&:after {
background: #ebbcba;
border: solid 4px currentColor;
box-sizing: border-box;
content: "";
display: inline-block;
height: 50%;
width: 50%;
}
&:before {
border-radius: 0 75% 75%;
transform: rotate(37.5deg) skew(30deg);
}
&:after {
border-radius: 75% 0 75% 75%;
transform: rotate(-37.5deg) skew(-30deg);
}
&:hover {
&:before {
animation: earwiggleleft 1s infinite;
}
&:after {
animation: earwiggleright 1s infinite;
}
}
}
}
</style>