이번 포스팅에는 이미지 에디터의 필수기능인 필터 기능을 구현해보도록 한다
// 컬러매트릭스 필터
interface FilterColormatrix {
name: string
filter: fabric.IBaseFilter
}
// 블랜드모드 parameter
interface BlendOptions {
mode?: BlendMode
color?: string
alpha?: number
}
// 블랜드모드 타입
type BlendMode = 'add'|'diff'|'subtract'|'multiply'|'screen'|'lighten'|'darken'|'overlay'|'exclusion'|'tint'
컬러매트릭스에 사용될 FilterColormatrix를 선언해주고 name과 filter를 할당해준다
블랜드 구현시 paramter로 mode, color, alpha값을 optional하게 받는 BlendOptions을 정의해주고 BlendMode에 들어갈 string type도 정의해준다
const matrices = {
Brownie: [
0.59970,0.34553,-0.27082,0,0.186,
-0.03770,0.86095,0.15059,0,-0.1449,
0.24113,-0.07441,0.44972,0,-0.02965,
0,0,0,1,0
],
Vintage: [
0.62793,0.32021,-0.03965,0,0.03784,
0.02578,0.64411,0.03259,0,0.02926,
0.04660,-0.08512,0.52416,0,0.02023,
0,0,0,1,0
],
Kodachrome: [
1.12855,-0.39673,-0.03992,0,0.24991,
-0.16404,1.08352,-0.05498,0,0.09698,
-0.16786,-0.56034,1.60148,0,0.13972,
0,0,0,1,0
],
Technicolor: [
1.91252,-0.85453,-0.09155,0,0.04624,
-0.30878,1.76589,-0.10601,0,-0.27589,
-0.23110,-0.75018,1.84759,0,0.12137,
0,0,0,1,0
],
Polaroid: [
1.438,-0.062,-0.062,0,0,
-0.122,1.378,-0.122,0,0,
-0.016,-0.016,1.483,0,0,
0,0,0,1,0
],
BlackWhite: [
1.5, 1.5, 1.5, 0, -1,
1.5, 1.5, 1.5, 0, -1,
1.5, 1.5, 1.5, 0, -1,
0, 0, 0, 1, 0,
]
}
Colormatrix에 사용될 값을 선언해주는데 이 값은 사전 정의된 canvas의 필터 값중 value가 존재하지 않는 일부값을 함께 포함되며 Grayscale, Invert ,Sepia가 있다 canvas 기본 필터는 아래 링크에서 확인할 수 있다
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter
CanvasRenderingContext2D.filter - Web APIs | MDN
The CanvasRenderingContext2D.filter property of the Canvas 2D API provides filter effects such as blurring and grayscaling. It is similar to the CSS filter property and accepts the same values.
developer.mozilla.org
컬러 매트릭스를 만드는 방법은 matrices의 객체 내부의 요소들의 배열 값은 각각 그 줄이 R,G,B,A를 뜻하는 4개의 줄로 이루어져있으며 그 4개의 줄의 안에 숫자는 다시 R, G, B, A와 shift 할 값을 의미하며 그 값만큼 해당 그림의 픽셀을 해당하는 컬러를 변조해주는 것이다
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix
<feColorMatrix> - SVG: Scalable Vector Graphics | MDN
The <feColorMatrix> SVG filter element changes colors based on a transformation matrix. Every pixel's color value [R,G,B,A] is matrix multiplied by a 5 by 5 color matrix to create new color [R',G',B',A'].
developer.mozilla.org
자세한 방법은 위 링크에서 확인할 수 있다
이제 클래스를 만들어주자
class Filter {
canvas: fabric.Canvas | null = null // 캔버스 객체
Colormatrix: Array<FilterColormatrix> = [] // 컬러매트릭스 필터
imageControl: Array<FilterColormatrix> = [] // 이미지 컨트롤 필터
image: fabric.Image | undefined = undefined // 이미지 객체
Blend = { // 블랜드모드
mode: '',
color: '#00FFFF',
alpha: 0.5
}
filters: Array<FilterColormatrix> = [] // 선택된 필터
class Filter 내에 canvas객체와 컬러 매트릭스를 담아둘 Colormatrix 배열, 이미지 필터를 담아둘 imageControl배열, 그리고 이미지 자체 객체인 Image와 블랜드 모드에 사용되는 Blend를 할당해준다
이때 구분을 위해서 분리하였지만 Colormatrix 와 imageControl 모두 canvas의 colorMartix이므로 FilterColormatrix 인터페이스로 정의한다
html에서 event를 전달하면 각각 Colormatrix, imageControl에서 값을 찾아서 적용해주는 형태로 만들어지고, 필터는 fabric.object 내의 filters 배열에 값을 넣어주는 형태로 적용되기 때문에 클래스에서도 filters를 별도의 배열로 만들어서 추가 삭제가 용이하게 한다
constructor(canvas: fabric.Canvas) {
// 필터를 초기화 해준다
this.canvas = canvas
this.Colormatrix = [
// 컬러매트릭스 필터
{name: 'Grayscale', filter: new fabric.Image.filters.Grayscale()},
{name: 'Invert', filter: new fabric.Image.filters.Invert()},
{name: 'Sepia', filter: new fabric.Image.filters.Sepia()},
]
for (let k in matrices) {
// 선언된 metrices를 순회하면서 필터를 추가해준다
const key = k as 'Brownie'|'Vintage'|'Kodachrome'|'Technicolor'|'Polaroid'|'BlackWhite'
this.Colormatrix.push({
name: key, filter: new fabric.Image.filters.ColorMatrix({ matrix: matrices[key] }),
})
}
this.imageControl = [
// 이미지 컨트롤 필터
{name: 'brightness', filter: new fabric.Image.filters.Brightness({brightness: 0})},
{name: 'contrast', filter: new fabric.Image.filters.Contrast({contrast: 0})},
{name: 'hue', filter: new fabric.Image.filters.HueRotation({rotation: 0})},
{name: 'saturation', filter: new fabric.Image.filters.Saturation({saturation: 0})},
{name: 'noise', filter: new fabric.Image.filters.Noise({noise: 5})},
{name: 'pixelate', filter: new fabric.Image.filters.Pixelate({blocksize: 5})},
{name: 'blur', filter: new fabric.Image.filters.Blur({blur: 0})},
]
}
초기화시 인스턴스에 전달된 캔버스를 할당하고 컬러 매트릭스 필터를 가져와 Colormatrix 배열에 담아준다
그리고 앞서 선언한 matrices를 for loop하여 각각 필터값을 class의 Colormatrix배열에 넣어주고 이미지 컨트롤 필터 배열도 초기화해준다
setFilter(name: string, checked: boolean) {
// 선택된 필터명과 체크여부가 전달되면
if (checked) {
// 체크됐다면 값을 찾아준다
const data = this.Colormatrix.find(x => x.name == name) || this.imageControl.find(x => x.name == name)
if (data) {
// 값을 찾았다면 필터에 업데이트 해준다
this.filters.push(data)
}
} else {
// 체크가 해제됐다면
const i = this.filters.findIndex(x => x.name == name)
if (i > -1) {
// 필터에서 제거해준다
this.filters.splice(i, 1)
}
}
this.filterUpdate()
}
선택된 필터를 배열에 세팅하는 메소드이다
전달되는 checked 값이 true라면 필터를 배열에 전달하고 false라면 배열에서 제거하도록 하는데 이때 name의 값으로 Colormatrix에서 한번 찾고 없다면 imageControl에서 한번 더 탐색하게된다
최종적으로 아예 존재하지 않는 값이라면 undefined가 될 것이기에 if문으로 조건체크를 하고 있다면 push하면된다
updateFilter
updateFilter (name: string, value: number) {
// 필터의 값이 조정되면 업데이트 해준다
const data = this.filters.find(x => x.name == name)
if (data) {
// 값을 찾았다면 필터에 업데이트 해준다
data.filter.setOptions({[name]: value})
this.filterUpdate()
}
}
setFilter메소드에서 필터값을 배열에 포함시킨경우 fabric.Object.filter.setOptions 함수를 활용하여 그 값을 업데이트한다
이후 클래스의 filterUpdate를 호출하여 필터를 업데이트해준다
changeBlandMode
changeBlandMode (options: BlendOptions) {
// 블랜드모드 변경
if (options.mode) this.Blend.mode = options.mode
if (options.color) this.Blend.color = options.color
if (options.alpha) this.Blend.alpha = options.alpha
if (this.Blend.mode) {
// 모드가 있는경우
const data = this.filters.find(x => x.name == 'BlendColor')
if (data) {
// 이미 적용된경우 업데이트
data.filter.setOptions({...this.Blend})
} else {
// 필터가 적용되지 않았다면 push
this.filters.push({
name: 'BlendColor',
filter: new fabric.Image.filters.BlendColor({...this.Blend})
})
}
this.filterUpdate()
} else {
// 모드가 없는경우 삭제하기
const i = this.filters.findIndex(x => x.name == 'BlendColor')
if (i > -1) {
this.filters.splice(i, 1)
}
this.filterUpdate()
}
}
parameter의 값이 모두 optional하기 때문에 값이 있다면 할당해주고, 반영된 이후 class의 Blend.mode의 값이 있다면 BlendColor라는 name으로 class의 filters에서 모드를 찾아준다
이때도 값이 있으면 fabric.Object.filter.setOptions함수로 값을 반영하고 없다면 filters에 name: 'BlendColor'으로 된 객체를 추가해준다
Blend.mode의 값이 없을땐 filters의 배열을 탐색하여 name: BlendColor객체를 삭제해준다
이후 filterUpdate메소드로 필터를 업데이트한다
filterUpdate
filterUpdate () {
// canvas 이미지의 필터값을 반영해준다
if (this.canvas) {
const object = this.canvas.getObjects().find(x => x.type == 'image') as fabric.Image
object.filters = this.filters.map(x => x.filter)
object.applyFilters()
this.canvas.requestRenderAll()
}
}
캔버스에서 type이 image인 객체를 찾는데 해당 함수는 항상 fabric.Object를 반환하기 때문에 as fabric.Image로 타입을 명시해준다
이후 객체의 filters는 class의 filter의 값으로 새로 채워주고 applyFilters함수로 필터를 적용해준다
이후 캔버스를 다시 랜더링한다
마지막으로 클래스가 사용될 수 있도록 선언해주자
fabric.filterBackend = new fabric.Canvas2dFilterBackend()
const canvas: fabric.Canvas = new fabric.Canvas('canvas')
const filter = new Filter(canvas)
일부 이미지가 너무 클때 필터가 동작하지 않거나 부분만 동작하는 문제가 발생할 수 있다
webgl의 textureSize를 늘려주거나 fabric.filterBackend = new fabric.Canvas2dFilterBackend()를 적용하면 해결된다(canvas2d가 더 느리다)
http://fabricjs.com/fabric-filters
Fabric.js filtering overview — Fabric.js Javascript Canvas Library
FabricJS filtering overview Introduction Fabric has a filtering engine that can work on both WEBGL or plain CPU javascript. Fabric has 2 classes to handle filtering, one is called WebglFilterBackend another is Canvas2dFilterBackend. The first time you get
fabricjs.com
fabric.filterBackend는 canvas의 getContext를 선언할때 사용되는 내용이 들어가는데 아래내용을 참고하면 된다
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
HTMLCanvasElement.getContext() - Web APIs | MDN
The HTMLCanvasElement.getContext() method returns a drawing context on the canvas, or null if the context identifier is not supported, or the canvas has already been set to a different context mode.
developer.mozilla.org
이로써 4회에 걸친 fabric.js 포스팅이 어느정도 마무리되었다.
객체를 선택, 히스토리, 펜그리기, flip, 그룹핑, 줌 등 구현할 기능은 많지만 추후 포스팅할 생각이나 요청이 있으면 추가하도록 하겠다
https://codepen.io/nkdevil/pen/gOddyGo
[fabric.js] Filter
...
codepen.io
이번포스팅 codepen (코드펜 특성상 이미지를 업로드해야 필터할 수 있어서 이미지업로드가 추가됨)
https://codesandbox.io/p/sandbox/svelte-image-editor-5f41q2
svelte_image_editor
CodeSandbox is an online editor tailored for web applications.
codesandbox.io
ui 적용되었고 sveltekit으로 제작된 이미지 에디터주소
fabric.js Image Editor - (3) Crop (0) | 2023.03.20 |
---|---|
fabric.js Image Editor - (2) Shape (0) | 2023.03.19 |
fabric.js Image Editor - (1) import image (1) | 2023.03.19 |
댓글 영역