目录
图片在前端的存储形式
图片以Uint8ClampedArray
的格式存储. 这是一个一维数组,每四位组成一个像素点,分别代表rgba
四个参数,每个参数的取值范围都是0~2^8-1
(0~255
).
// 这是一个全黑色,透明的像素点
const pixel = new Uint8ClampedArrray([0,0,0,255]);
一张图片就是由 宽 x 高 个这样的像素点组成的.
前端上传图片
图片的容器标签是<img />
,在JavaScript中可以用new Image()
来构造.
export function createImageElement(src) {
const img = new Image();
img.src = src;
return img;
}
前端选择文件,都通过<input type="file"/>
来完成,对于选中的图片文件,使用FileReader
的readAsDataURL
方法读取.
const reader = new FileReader();
reader.onload = () => {
const img = createImageElement(reader.result);
};
reader.readAsDataURL(file);
这里为了方便DOM操作,用svelte
框架来快速实现一个图片上传器.
<script>
import { createEventDispatcher, onMount, tick } from "svelte";
import { createImageElement } from "../../utils";
export let size = 50;
let image;
let imgSrc;
let inputDom;
let container;
onMount(() => {
container.style.setProperty("--trigger-size", size + "px");
});
const dispatch = createEventDispatcher();
const pickHandler = (event) => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async () => {
imgSrc = reader.result;
const img = createImageElement(reader.result);
await tick();
dispatch("picked", {
preview: image,
src: reader.result,
img,
file,
event,
});
};
reader.readAsDataURL(file);
};
const triggerClick = () => {
inputDom.click();
};
</script>
<div bind:this={container} class="image-picker_container">
<div on:click|stopPropagation={triggerClick} class="image-picker_trigger">
{#if imgSrc}
<img
bind:this={image}
src={imgSrc}
alt="Error"
class="image-picker_preview"
/>
{:else}
<div class="image-picker_cross" />
{/if}
<input
bind:this={inputDom}
type="file"
on:change={pickHandler}
class="image-picker_input-hidden"
/>
</div>
</div>
<style>
.image-picker_container {
--trigger-padding: 4px;
--trigger-border: 1px;
--cross-size: calc(var(--trigger-size) * 0.6);
}
.image-picker_preview {
width: var(--trigger-size);
height: var(--trigger-size);
object-fit: contain;
}
.image-picker_input-hidden {
display: none;
}
.image-picker_trigger {
border: var(--trigger-border) dashed;
padding: var(--trigger-padding);
height: var(--trigger-size);
width: var(--trigger-size);
position: relative;
}
.image-picker_cross::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: var(--cross-size);
border: calc((var(--trigger-size) * 0.2) / 2) solid #ccc;
}
.image-picker_cross::before {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-