226 lines
9 KiB
JavaScript
226 lines
9 KiB
JavaScript
import { useContext, useState } from 'react'
|
|
import { ImageIcon, Wand2 } from 'lucide-react'
|
|
import { fabric } from 'fabric'
|
|
import CanvasContext from '@/components/Context/canvasContext/CanvasContext'
|
|
import { Label } from '@/components/ui/label'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Slider } from '@/components/ui/slider'
|
|
import { Separator } from '@/components/ui/separator'
|
|
|
|
const ImageCustomization = () => {
|
|
const { canvas } = useContext(CanvasContext)
|
|
const [shadowColor, setShadowColor] = useState("#000000")
|
|
const [highlightColor, setHighlightColor] = useState("#ffffff")
|
|
const [blendMode, setBlendMode] = useState("multiply")
|
|
const [filterType, setFilterType] = useState("sepia")
|
|
const [filterIntensity, setFilterIntensity] = useState(0.5)
|
|
|
|
const applyDuotoneFilter = () => {
|
|
if (!canvas) return
|
|
const activeObject = canvas.getActiveObject()
|
|
if (!activeObject || activeObject.type !== "image") {
|
|
console.warn("No active image object selected.")
|
|
return
|
|
}
|
|
activeObject.filters = [
|
|
new fabric.Image.filters.BlendColor({
|
|
color: shadowColor,
|
|
mode: blendMode,
|
|
}),
|
|
new fabric.Image.filters.BlendColor({
|
|
color: highlightColor,
|
|
mode: "screen",
|
|
}),
|
|
]
|
|
activeObject.applyFilters()
|
|
canvas.renderAll()
|
|
}
|
|
|
|
const createFilter = (value) => {
|
|
let effect
|
|
switch (value) {
|
|
case 'sepia':
|
|
effect = new fabric.Image.filters.Sepia()
|
|
break
|
|
case 'contrast':
|
|
effect = new fabric.Image.filters.Contrast({ contrast: filterIntensity })
|
|
break
|
|
case 'brightness':
|
|
effect = new fabric.Image.filters.Brightness({ brightness: filterIntensity })
|
|
break
|
|
case 'grayscale':
|
|
effect = new fabric.Image.filters.Grayscale()
|
|
break
|
|
case 'invert':
|
|
effect = new fabric.Image.filters.Invert()
|
|
break
|
|
case 'blur':
|
|
effect = new fabric.Image.filters.Blur({
|
|
blur: filterIntensity,
|
|
})
|
|
break
|
|
case 'pixelate':
|
|
effect = new fabric.Image.filters.Pixelate({
|
|
blocksize: filterIntensity * 10,
|
|
})
|
|
break
|
|
case 'huerotation':
|
|
effect = new fabric.Image.filters.HueRotation({
|
|
rotation: filterIntensity * 360,
|
|
})
|
|
break
|
|
case 'noise':
|
|
effect = new fabric.Image.filters.Noise({
|
|
noise: filterIntensity * 100,
|
|
})
|
|
break
|
|
default:
|
|
effect = null
|
|
}
|
|
return effect
|
|
}
|
|
|
|
const applyFilter = () => {
|
|
if (!canvas) return
|
|
const activeObject = canvas.getActiveObject()
|
|
if (!activeObject || activeObject.type !== "image") {
|
|
console.warn("No active image object selected.")
|
|
return
|
|
}
|
|
const filter = createFilter(filterType)
|
|
if (filter) {
|
|
activeObject.filters = [filter]
|
|
activeObject.applyFilters()
|
|
canvas.renderAll()
|
|
}
|
|
}
|
|
|
|
const revertFilters = () => {
|
|
if (!canvas) return
|
|
const image = canvas.getActiveObject()
|
|
if (image) {
|
|
image.filters = []
|
|
image.applyFilters()
|
|
canvas.renderAll()
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
<h2 className="font-semibold my-2">Blend filter</h2>
|
|
<div className="grid grid-cols-1 gap-4">
|
|
<div className="space-y-2">
|
|
<Label>Shadow Color</Label>
|
|
<div className="flex items-center space-x-2">
|
|
<Input
|
|
type="color"
|
|
value={shadowColor}
|
|
onChange={(e) => setShadowColor(e.target.value)}
|
|
className="w-12 h-12 p-1 rounded-md"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={shadowColor}
|
|
onChange={(e) => setShadowColor(e.target.value)}
|
|
className="flex-grow"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Highlight Color</Label>
|
|
<div className="flex items-center space-x-2">
|
|
<Input
|
|
type="color"
|
|
value={highlightColor}
|
|
onChange={(e) => setHighlightColor(e.target.value)}
|
|
className="w-12 h-12 p-1 rounded-md"
|
|
/>
|
|
<Input
|
|
type="text"
|
|
value={highlightColor}
|
|
onChange={(e) => setHighlightColor(e.target.value)}
|
|
className="flex-grow"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Blend Mode</Label>
|
|
<Select value={blendMode} onValueChange={setBlendMode}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="multiply">Multiply</SelectItem>
|
|
<SelectItem value="screen">Screen</SelectItem>
|
|
<SelectItem value="overlay">Overlay</SelectItem>
|
|
<SelectItem value="darken">Darken</SelectItem>
|
|
<SelectItem value="lighten">Lighten</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="grid gap-2">
|
|
<Button onClick={applyDuotoneFilter} className="flex-1">
|
|
<Wand2 className="mr-2" />
|
|
Apply Duotone
|
|
</Button>
|
|
<Button variant="outline" onClick={revertFilters} className="flex-1">
|
|
<ImageIcon className="mr-2" />
|
|
Revert Filters
|
|
</Button>
|
|
</div>
|
|
|
|
<div>
|
|
<Separator className="mt-4" />
|
|
<h2 className="font-semibold my-2">Apply filter</h2>
|
|
<div className='grid gap-4'>
|
|
<div className="space-y-2">
|
|
<Label>Filter Type</Label>
|
|
<Select value={filterType} onValueChange={setFilterType}>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="sepia">Sepia</SelectItem>
|
|
<SelectItem value="contrast">Contrast</SelectItem>
|
|
<SelectItem value="brightness">Brightness</SelectItem>
|
|
<SelectItem value="grayscale">Grayscale</SelectItem>
|
|
<SelectItem value="invert">Invert</SelectItem>
|
|
<SelectItem value="blur">Blur</SelectItem>
|
|
<SelectItem value="pixelate">Pixelate</SelectItem>
|
|
<SelectItem value="huerotation">Hue Rotation</SelectItem>
|
|
<SelectItem value="noise">Noise</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label>Filter Intensity</Label>
|
|
<Slider
|
|
min={0}
|
|
max={1}
|
|
step={0.01}
|
|
value={[filterIntensity]}
|
|
onValueChange={([value]) => setFilterIntensity(value)}
|
|
/>
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Button onClick={applyFilter} className="flex-1">
|
|
<Wand2 className="mr-2" />
|
|
Apply Filter
|
|
</Button>
|
|
<Button variant="outline" onClick={revertFilters} className="flex-1">
|
|
<ImageIcon className="mr-2" />
|
|
Revert Filters
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default ImageCustomization
|
|
|