fix: don't stretch activity top author image (#10556)
This is a followup to !10524. In addition I changed that the tooltip triggers for the whole height, instead for only the bar height, because otherwise it is esp for small bars nearly impossible to get the tooltip to open. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10556 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Beowulf <beowulf@beocode.eu> Co-committed-by: Beowulf <beowulf@beocode.eu>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import {flushPromises, mount} from '@vue/test-utils';
|
||||
import RepoActivityTopAuthors from './RepoActivityTopAuthors.vue';
|
||||
import {expect, test, vi} from 'vitest';
|
||||
|
||||
test('calc image size and shift', async () => {
|
||||
vi.spyOn(RepoActivityTopAuthors.methods, 'init').mockResolvedValue({});
|
||||
|
||||
const repoActivityTopAuthors = mount(RepoActivityTopAuthors, {
|
||||
props: {
|
||||
locale: {
|
||||
commitActivity: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
const square = repoActivityTopAuthors.vm.calcImageSizeAndShift({naturalWidth: 50, naturalHeight: 50});
|
||||
expect(square).toEqual([20, 20, 0, 0]);
|
||||
|
||||
const portrait = repoActivityTopAuthors.vm.calcImageSizeAndShift({naturalWidth: 5, naturalHeight: 50});
|
||||
expect(portrait).toEqual([2, 20, 9, 0]);
|
||||
|
||||
const landscape = repoActivityTopAuthors.vm.calcImageSizeAndShift({naturalWidth: 500, naturalHeight: 5});
|
||||
expect(landscape).toEqual([20, 0.2, 0, 9.9]);
|
||||
});
|
||||
@@ -42,56 +42,7 @@ export default {
|
||||
i18nCommitActivity: this,
|
||||
}),
|
||||
mounted() {
|
||||
const refStyle = window.getComputedStyle(this.$refs.style);
|
||||
this.colors.barColor = refStyle.backgroundColor;
|
||||
|
||||
for (const item of this.activityTopAuthors) {
|
||||
const img = new Image();
|
||||
img.src = item.avatar_link;
|
||||
item.avatar_img = img;
|
||||
}
|
||||
|
||||
Chart.register({
|
||||
id: 'image_label',
|
||||
afterDraw: (chart) => {
|
||||
const xAxis = chart.boxes[0];
|
||||
const yAxis = chart.boxes[1];
|
||||
for (const [index] of xAxis.ticks.entries()) {
|
||||
const x = xAxis.getPixelForTick(index);
|
||||
const img = this.activityTopAuthors[index].avatar_img;
|
||||
|
||||
chart.ctx.save();
|
||||
chart.ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, x - 10, yAxis.bottom + 10, 20, 20);
|
||||
chart.ctx.restore();
|
||||
}
|
||||
},
|
||||
beforeEvent: (chart, args) => {
|
||||
const event = args.event;
|
||||
if (event.type !== 'mousemove' && event.type !== 'click') return;
|
||||
|
||||
const yAxis = chart.boxes[1];
|
||||
if (event.y < yAxis.bottom + 10 || event.y > yAxis.bottom + 30) {
|
||||
chart.canvas.style.cursor = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const xAxis = chart.boxes[0];
|
||||
const pointIdx = xAxis.ticks.findIndex((_, index) => {
|
||||
const x = xAxis.getPixelForTick(index);
|
||||
return event.x >= x - 10 && event.x <= x + 10;
|
||||
});
|
||||
|
||||
if (pointIdx === -1) {
|
||||
chart.canvas.style.cursor = '';
|
||||
return;
|
||||
}
|
||||
|
||||
chart.canvas.style.cursor = 'pointer';
|
||||
if (event.type === 'click' && this.activityTopAuthors[pointIdx].home_link) {
|
||||
window.location.href = this.activityTopAuthors[pointIdx].home_link;
|
||||
}
|
||||
},
|
||||
});
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
graphPoints() {
|
||||
@@ -137,8 +88,82 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
intersect: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
init() {
|
||||
const refStyle = window.getComputedStyle(this.$refs.style);
|
||||
this.colors.barColor = refStyle.backgroundColor;
|
||||
|
||||
for (const item of this.activityTopAuthors) {
|
||||
const img = new Image();
|
||||
img.src = item.avatar_link;
|
||||
item.avatar_img = img;
|
||||
}
|
||||
|
||||
Chart.register({
|
||||
id: 'image_label',
|
||||
afterDraw: (chart) => {
|
||||
const xAxis = chart.boxes[0];
|
||||
const yAxis = chart.boxes[1];
|
||||
for (const [index] of xAxis.ticks.entries()) {
|
||||
const x = xAxis.getPixelForTick(index);
|
||||
const img = this.activityTopAuthors[index].avatar_img;
|
||||
|
||||
chart.ctx.save();
|
||||
const [width, height, dx, dy] = this.calcImageSizeAndShift(img);
|
||||
chart.ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, x - 10 + dx, yAxis.bottom + 10 + dy, width, height);
|
||||
chart.ctx.restore();
|
||||
}
|
||||
},
|
||||
beforeEvent: (chart, args) => {
|
||||
const event = args.event;
|
||||
if (event.type !== 'mousemove' && event.type !== 'click') return;
|
||||
|
||||
const yAxis = chart.boxes[1];
|
||||
if (event.y < yAxis.bottom + 10 || event.y > yAxis.bottom + 30) {
|
||||
chart.canvas.style.cursor = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const xAxis = chart.boxes[0];
|
||||
const pointIdx = xAxis.ticks.findIndex((_, index) => {
|
||||
const x = xAxis.getPixelForTick(index);
|
||||
return event.x >= x - 10 && event.x <= x + 10;
|
||||
});
|
||||
|
||||
if (pointIdx === -1) {
|
||||
chart.canvas.style.cursor = '';
|
||||
return;
|
||||
}
|
||||
|
||||
chart.canvas.style.cursor = 'pointer';
|
||||
if (event.type === 'click' && this.activityTopAuthors[pointIdx].home_link) {
|
||||
window.location.href = this.activityTopAuthors[pointIdx].home_link;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
calcImageSizeAndShift(img) {
|
||||
const targetSize = 20;
|
||||
const [imgWidth, imgHeight] = [img.naturalWidth, img.naturalHeight];
|
||||
|
||||
// The image should be contained in a square,
|
||||
// so the scale depends on the longer dimension.
|
||||
const scale = targetSize / (Math.max(imgWidth, imgHeight));
|
||||
const calcScale = (size) => size * scale;
|
||||
const [width, height] = [calcScale(imgWidth), calcScale(imgHeight)];
|
||||
|
||||
// The image should be centered in the 20x20 square.
|
||||
const calcShift = (size) => (targetSize - size) / 2;
|
||||
const [dx, dy] = [calcShift(width), calcShift(height)];
|
||||
|
||||
return [width, height, dx, dy];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user