基於像素完美的基於Web的MS Paint Remake等...嘗試一下!然後加入Discord服務器分享您的藝術品!
JS Paint重新創建了MS油漆的每個工具和菜單,甚至還鮮為人知的功能,以高度的保真度。
它支持主題,其他文件類型和可訪問性功能,例如眼目光模式和語音識別。
啊,是的,好老油漆。不是帶有絲帶或新的Skeuomormormormormorphic的界面的那個,可以佔據近一半的屏幕。 (而不是更新的油漆3D。)
Windows 95、98和XP是塗料的黃金年。您有一個工具箱和一個顏色盒,一個前景顏色和背景顏色,這就是您所需要的。
事情很簡單。
但是我們想撤消三個以上的動作。我們想編輯透明圖像。我們不能只繼續使用舊油漆。
這就是為什麼我要製作JS油漆。我想把優質的舊油漆帶入現代時代。
編輯功能:
其他改進:
使用工具的一些事情尚未完成。請參閱todo.md
Web應用程序中的完整剪貼板支持需要一個瀏覽器支持帶有images的異步剪貼板API,即在撰寫時76+ Chrome。
在其他瀏覽器中,您仍然可以使用CTRL+C複製,用CTRL+X切割,然後用Ctrl+V粘貼,但是從JS塗料中復制的數據只能粘貼到其他JS塗料的實例中。外部圖像可以粘貼到中。
與MS Paint不同,您可以使用Edit> Undo來恢復節省的色彩或質量降低。這不會撤消文件,但允許您使用文件>另存為更高質量的不同格式。
建議保存為PNG,因為它可以在保留全質量的同時提供小文件尺寸。
文件擴展 | 姓名 | 讀 | 寫 | 閱讀調色板 | 寫調色板 |
---|---|---|---|---|---|
.png | PNG | ✅ | ✅ | ||
.bmp,.dib | 單色位圖 | ✅ | ✅ | ✅ | |
.bmp,.dib | 16顏色位圖 | ✅ | ✅ | ✅ | |
.bmp,.dib | 256顏色位圖 | ✅ | ✅ | ✅ | |
.bmp,.dib | 24位位圖 | ✅ | ✅ | N/A。 | N/A。 |
.tif,.tiff,.dng,.cr2,.nef | TIFF(加載第一頁) | ✅ | ✅ | ||
PDF(加載第一頁) | ✅ | ||||
.webp | WebP | ||||
.gif | GIF | ||||
.jpeg,.jpg | jpeg | N/A。 | N/A。 | ||
.svg | SVG(僅默認尺寸) | ||||
.ico | ICO(僅默認尺寸) |
當前,標記的功能保留到瀏覽器中以支持或不支持。如果標記了“寫入”,則格式將出現在文件類型下拉列表中,但在嘗試保存時可能不起作用。有關打開文件,請參見Wikipedia的瀏覽器映像格式支持表,以獲取更多信息。
標記的功能可能很快即將到來,不適用的N/A手段。
“ Read Palette”是指自動將顏色加載到顏色框中(從索引的顏色圖像),“ Write Palette”是指編寫索引的顏色圖像。
使用顏色>保存顏色和顏色>獲得顏色,您可以以許多不同格式保存和加載顏色,以兼容各種程序。
如果您想在另一個應用程序中添加廣泛的調色板支持,我將此功能作為庫可用: Anypalette.js
文件擴展 | 姓名 | 程式 | 讀 | 寫 |
---|---|---|---|---|
。朋友 | 即興調色板 | Windows 95和Windows NT 4.0的MS油漆 | ✅ | ✅ |
.gpl | gimp調色板 | gimp,inkscape,krita,kolourpaint,scribus,cinepaint,mypaint | ✅ | ✅ |
.aco | Adobe顏色色板 | Adobe Photoshop | ✅ | ✅ |
.ase | Adobe Swatch Exchange | Adobe Photoshop,Indesign和Illustrator | ✅ | ✅ |
。TXT | paint.net調色板 | paint.net | ✅ | ✅ |
。行為 | Adobe顏色表 | Adobe Photoshop和Illustrator | ✅ | ✅ |
.pal,.psppalette | 油漆店Pro調色板 | Paint Shop Pro(JASC軟件 / Corel) | ✅ | ✅ |
.hpl | 家庭調色板 | Allaire Homesite / Macromedia Coldfusion | ✅ | ✅ |
。CS | Colorschemer | Colorschemer Studio | ✅ | |
。朋友 | 星際船調色板 | 星際爭霸 | ✅ | ✅ |
.wpe | 星形地形調色板 | 星際爭霸 | ✅ | ✅ |
.sketchpalette | 草圖調色板 | 草圖 | ✅ | ✅ |
.spl | 滑雪調色板 | Skencil(以前稱為素描) | ✅ | ✅ |
.soc | Staroffice顏色 | Staroffice,OpenOffice,Libreoffice | ✅ | ✅ |
。顏色 | KolourPaint Color Collection | Kolourpaint | ✅ | ✅ |
。顏色 | 等離子桌面配色方案 | KDE等離子桌面 | ✅ | |
。主題 | Windows主題 | Windows桌面 | ✅ | |
.themepack | Windows主題 | Windows桌面 | ✅ | |
.css,.scss,.styl | 級聯樣式表 | 網絡瀏覽器 /網頁 | ✅ | ✅ |
.html,.svg,.js | 任何具有CSS顏色的文本文件 | 網絡瀏覽器 /網頁 | ✅ |
有一個黑色和白色模式,帶有圖案,而不是調色板中的顏色,您可以從圖像>屬性...
如果您在正確的位置抓住顏色框和工具箱,則可以將其拖動。您甚至可以將它們拖到小窗戶中。您可以雙擊其標題欄,將窗戶放回側面。
除了左鍵單擊前景顏色和右鍵單擊背景顏色外,還可以通過繪製CTRL訪問第三種顏色。它從沒有顏色開始,因此您需要保持CTRL並首先選擇顏色。關於此顏色插槽的奇特之處在於,您可以在繪製時按和釋放CTRL以切換顏色。
您可以將圖像轉換(例如翻轉/旋轉,伸展/偏斜或反轉(在圖像菜單中)應用於整個圖像或選擇。嘗試使用自由形式的選擇工具塗抹,然後進行圖像>倒置
這些MS油漆教程中的技巧和技巧也可以在JS Paint中起作用:
JS油漆可以安裝為漸進式網絡應用程序(PWA),儘管它尚未脫機。在地址欄中查找安裝提示。
PWA功能:
缺少功能:
我還將其置於帶電子和電子鍛造的桌面應用程序中。您可以從“發行”頁面下載它。
電子應用功能:
jspaint path/to/file.png
editor_window.on("close")
調用preventDefault
,可能是一個功能,但需要顯示/聚焦窗口克隆倉庫。
如果沒有它,請安裝Node.js,然後在項目目錄中打開命令提示符 /終端。
運行npm run lint
以檢查拼寫錯誤,類型錯誤,代碼樣式問題和其他問題。
運行npm run format
以自動修復格式問題,或npx eslint --fix
修復所有可自動固定問題的文件。
格式規則配置為與VS Code的內置格式化器的兼容性。
運行npm test
以使用柏樹運行基於瀏覽器的測試。 (不幸的是,啟動和運行測試很慢。)
運行npm run accept
以接受任何視覺更改。不幸的是,這重新運行了所有測試,而不是接受先前測試的結果,因此您最終可能會得到與以前的測試不同的結果。如果使用GitHub桌面,則可以以四種不同的模式查看圖像的差異。
要打開Cypress UI,請先運行npm run test:start-server
,然後同時進行npm run cy:open
測試還與Travis CI連續集成。
使用npm i
安裝依賴關係後,請使用npm run dev
啟動實時填充服務器。
確保在layout.css
中使用任何佈局重要樣式。在更新layout.css
時,使用RTLCSS生成了樣式表的左右版本。
您應該通過將語言更改為阿拉伯語或希伯來語來測試RTL佈局。轉到Extras>“語言>ال樣”或“עברעבר” 。
有關如何控制RTL佈局,請參見控制指令。
有一個VS代碼啟動任務,用於附加到Chrome進行調試。有關使用說明,請參見.vscode/launch.json
。
npm i
安裝依賴項npm run electron:start
包括電子選,因此您可以使用F5 / Ctrl+R進行重新加載,而F12 / Ctrl+Shift+I打開DevTools。
您可以使用npm run electron:make
有一個VS代碼啟動任務,用於調試電子主過程。對於渲染器過程,您可以使用嵌入式的Chrome DevTools。
可以使用常規Web服務器部署JS塗料。
沒有什麼需要編譯的。
可選地,如果將URL粘貼到JS Paint中,則可以設置Anywhere Server的CORS Anywhere Server,或使用#load:<URL>
功能,其中不在同一域上。
默認情況下,它將使用設置的Any Ancome cors使用cors。
它是在Heroku上免費託管的,您可以設置自己的實例並將其配置為與自己的域一起使用。
您必須使用自己的實例URL查找並替換https://jspaint-cors-proxy.herokuapp.com
。
多人支持當前依賴於不是開源軟件的Firebase。
您可以創建一個Firebase實時數據庫實例,並編輯JS Paint的sessions.js
指向它,在設置Web應用程序時,用Firebase Console替換了傳遞給initializeApp
config
。
但是到目前為止,多人遊戲模式非常卑鄙。應該用開源的東西代替,更安全,更高效,更健壯。
將其添加到您的HTML:
< iframe src =" https://jspaint.app " width =" 100% " height =" 100% " > </ iframe >
您可以通過向URL添加#load:<URL>
從URL加載圖像。
< iframe src =" https://jspaint.app#load:https://jspaint.app/favicon.ico " width =" 100% " height =" 100% " > </ iframe >
如果要控制JS塗料,如何保存/加載文件或直接訪問畫布,則有一個不穩定的API。
首先,您需要克隆回購,因此您可以將iframe
指向本地副本。
JS油漆的本地副本必須託管與包含頁面同一Web服務器,或者更具體地說,必須共享相同的來源。
擁有本地副本也意味著API隨時都不會破裂。
如果將JS塗料克隆到一個名為jspaint
的文件夾中,該文件夾與要嵌入的頁面相同的文件夾中,您可以使用此信息:
< iframe src =" jspaint/index.html " id =" jspaint-iframe " width =" 100% " height =" 100% " > </ iframe >
如果它居住在其他地方,則可能需要添加../
到路徑的開始,才能上升一個水平。例如, src="../../apps/jspaint/index.html"
。您也可以使用絕對URL,例如src="https://example.com/cool-apps/jspaint/index.html"
。
您可以使用JS Paint的systemHooks
API覆蓋文件保存和打開對話框。
< script >
var iframe = document . getElementById ( "jspaint-iframe" ) ;
var jspaint = iframe . contentWindow ;
// Wait for systemHooks object to exist (the iframe needs to load)
waitUntil ( ( ) => jspaint . systemHooks , 500 , ( ) => {
// Hook in
jspaint . systemHooks . showSaveFileDialog = async ( { formats , defaultFileName , defaultPath , defaultFileFormatID , getBlob , savedCallbackUnreliable , dialogTitle } ) => { ... } ;
jspaint . systemHooks . showOpenFileDialog = async ( { formats } ) => { ... } ;
jspaint . systemHooks . writeBlobToHandle = async ( save_file_handle , blob ) => { ... } ;
jspaint . systemHooks . readBlobFromHandle = async ( file_handle ) => { ... } ;
} ) ;
// General function to wait for a condition to be met, checking at regular intervals
function waitUntil ( test , interval , callback ) {
if ( test ( ) ) {
callback ( ) ;
} else {
setTimeout ( waitUntil , interval , test , interval , callback ) ;
}
}
</ script >
斑點代表內存中文件的內容。
文件句柄是可以識別文件的任何東西。您可以擁有此概念,並定義如何識別文件。從索引到數組到Dropbox文件ID,再到IPFS URL,再到文件路徑,它可能是任何東西。我忘記了它可以是任何類型,也許它可能是字符串。
一旦有了文件句柄的概念,就可以使用系統掛鉤實現文件拾取器,以及函數以讀寫文件。
命令 | 使用的鉤子 |
---|---|
文件>另存為 | systemHooks.showSaveFileDialog ,然後選擇文件時, systemHooks.writeBlobToHandle |
文件>打開 | systemHooks.showOpenFileDialog ,然後選擇文件時, systemHooks.readBlobFromHandle |
文件>保存 | systemHooks.writeBlobToHandle (或與文件相同>保存,如尚未打開文件) |
編輯>複製到 | systemHooks.showSaveFileDialog ,然後選擇文件時, systemHooks.writeBlobToHandle |
編輯>粘貼 | systemHooks.showOpenFileDialog ,然後選擇文件時, systemHooks.readBlobFromHandle |
文件>設置為牆紙(瓷磚) | systemHooks.setWallpaperTiled 如果定義,else systemHooks.setWallpaperCentered 如果定義,則與文件>另存為 |
文件>設置為牆紙(中心) | systemHooks.setWallpaperCentered 如果定義,則與文件>另存為 |
Extras>渲染歷史為GIF | 與文件相同>另存為 |
顏色>保存顏色 | 與文件相同>另存為 |
顏色>獲得顏色 | 與文件相同>打開 |
要使用加載用於編輯的文件啟動該應用程序,請等待應用程序加載,然後使用文件句柄systemHooks.readBlobFromHandle
調用,並告訴應用程序加載該文件blob。
const file_handle = "initial-file-to-load" ;
systemHooks . readBlobFromHandle ( file_handle ) . then ( file => {
if ( file ) {
contentWindow . open_from_file ( file , file_handle ) ;
}
} , ( error ) => {
// Note: in some cases, this handler may not be called, and instead an error message is shown by readBlobFromHandle directly.
contentWindow . show_error_message ( `Failed to open file ${ file_handle } ` , error ) ;
} ) ;
這很笨拙,將來可能會有一個查詢字符串參數來通過其句柄加載初始文件。 (自我注意:它需要等待您的系統掛鉤註冊,以某種方式。)
已經有一個查詢字符串參數可以從URL加載:
< iframe src =" https://jspaint.app?load:SOME_URL_HERE " > </ iframe >
但這不會設置用於保存的文件句柄。
您可以定義兩個功能來設置牆紙,將通過文件>設置為牆紙(瓷磚)和文件>設置為牆紙(中心) 。
systemHooks.setWallpaperTiled
= (canvas) => { ... };
systemHooks.setWallpaperCentered
= (canvas) => { ... };
如果僅定義systemHooks.setWallpaperCentered
,JS Paint將嘗試猜測屏幕的尺寸並鋪圖像,並通過調用systemHooks.setWallpaperCentered
功能應用它。
如果您不指定systemHooks.setWallpaperCentered
,則使用systemHooks.showSaveFileDialog
和systemHooks.writeBlobToHandle
保存文件( <original file name> wallpaper.png
)。
這是一個完整的示例,支持持續的自定義牆紙作為包含頁面上的背景:
const wallpaper = document . querySelector ( "body" ) ; // or some other element
jspaint . systemHooks . setWallpaperCentered = ( canvas ) => {
canvas . toBlob ( ( blob ) => {
setDesktopWallpaper ( blob , "no-repeat" , true ) ;
} ) ;
} ;
jspaint . systemHooks . setWallpaperTiled = ( canvas ) => {
canvas . toBlob ( ( blob ) => {
setDesktopWallpaper ( blob , "repeat" , true ) ;
} ) ;
} ;
function setDesktopWallpaper ( file , repeat , saveToLocalStorage ) {
const blob_url = URL . createObjectURL ( file ) ;
wallpaper . style . backgroundImage = `url( ${ blob_url } )` ;
wallpaper . style . backgroundRepeat = repeat ;
wallpaper . style . backgroundPosition = "center" ;
wallpaper . style . backgroundSize = "auto" ;
if ( saveToLocalStorage ) {
const fileReader = new FileReader ( ) ;
fileReader . onload = ( ) => {
localStorage . setItem ( "wallpaper-data-url" , fileReader . result ) ;
localStorage . setItem ( "wallpaper-repeat" , repeat ) ;
} ;
fileReader . onerror = ( ) => {
console . error ( "Error reading file (for setting wallpaper)" , file ) ;
} ;
fileReader . readAsDataURL ( file ) ;
}
}
// Initialize the wallpaper from localStorage, if it exists
try {
const wallpaper_data_url = localStorage . getItem ( "wallpaper-data-url" ) ;
const wallpaper_repeat = localStorage . getItem ( "wallpaper-repeat" ) ;
if ( wallpaper_data_url ) {
fetch ( wallpaper_data_url ) . then ( response => response . blob ( ) ) . then ( file => {
setDesktopWallpaper ( file , wallpaper_repeat , false ) ;
} ) ;
}
} catch ( error ) {
console . error ( error ) ;
}
有點遞歸,對不起;可能會更簡單。就像僅使用數據URL。 (實際上,我認為我想使用blob URL,以免它用超長的URL膨脹DOM Insportor。這實際上是DevTools UX錯誤。也許他們已經改善了這一點?)
您可以加載具有所需尺寸的文件。目前沒有特殊的API。
請參閱最初加載文件。
您可以以編程方式更改主題:
var iframe = document . getElementById ( "jspaint-iframe" ) ;
var jspaint = iframe . contentWindow ;
jspaint . set_theme ( "modern.css" ) ;
但這將破壞用戶的喜好。
Extras>“主題”菜單仍然可以使用,但是在重新加載頁面時,偏好不會持續。
將來可能會有一個查詢字符串參數來指定默認主題。您還可以分配J -Spraint更改默認主題。
與主題類似,您可以嘗試以編程方式更改語言:
var iframe = document . getElementById ( "jspaint-iframe" ) ;
var jspaint = iframe . contentWindow ;
jspaint . set_language ( "ar" ) ;
但這實際上會要求用戶重新加載應用程序以更改語言。
Extras>“語言”菜單仍將起作用,但是每次重新加載頁面時,用戶都會不願更改語言。
將來可能會有一個查詢字符串參數來指定默認語言。您還可以分配J -Spraint來更改默認語言。
尚未支持。您可以分叉J -Spraint並添加自己的菜單。
通過訪問畫布,您可以實現圖紙的實時預覽,例如,實時更新遊戲引擎中的紋理。
var iframe = document . getElementById ( "jspaint-iframe" ) ;
// contentDocument here refers to the webpage loaded in the iframe, not the image document loaded in jspaint.
// We're just reaching inside the iframe to get the canvas.
var canvas = iframe . contentDocument . querySelector ( ".main-canvas" ) ;
建議不要將其用於加載文檔,因為它不會更改文檔標題或重置撤消/重做歷史記錄等。而是使用open_from_file
。
如果您想製作按鈕或其他UI來對文檔進行操作,則應該(可能)使其變得不可行。這很容易,只需將您的動作包裹在undoable
中。
var iframe = document . getElementById ( "jspaint-iframe" ) ;
var jspaint = iframe . contentWindow ;
var icon = new Image ( ) ;
icon . src = "some-folder/some-image-15x11-pixels.png" ;
jspaint . undoable ( {
name : "Seam Carve" ,
icon : icon , // optional
} , function ( ) {
// do something to the canvas
} ) ;
systemHooks.showSaveFileDialog({ formats, defaultFileName, defaultPath, defaultFileFormatID, getBlob, savedCallbackUnreliable, dialogTitle })
定義此功能以覆蓋默認保存對話框。這既用於保存圖像,調色板文件和動畫。
參數:
formats
:代表文件類型的對像數組,並具有以下屬性:formatID
:唯一標識格式的字符串(可能與mimeType
相同)mimeType
(可選):文件格式的指定媒體類型,例如"image/png"
(調色板格式沒有此屬性)name
:文件格式的名稱,例如"WebP"
nameWithExtensions
:文件格式的名稱,然後是擴展名列表,例如"TIFF (*.tif;*.tiff)"
extensions
:一個文件擴展名,不包括DOT,首選擴展名,例如["bmp", "dib"]
defaultFileName
(可選):建議的文件名,例如"Untitled.png"
或打開文檔的名稱。defaultPath
(可選):打開的文檔的文件句柄,因此您可以輕鬆地將其保存到同一文件夾中。 MISNOMER:這可能不是一條路徑,這取決於您如何定義文件句柄。defaultFileFormatID
(可選):默認情況下選擇文件格式的formatID
。async function getBlob(formatID)
:您調用的函數以以一種受支持格式之一獲取文件。它採用formatID
,並返回一個Promise
,該諾言用Blob
代表要保存的文件內容。function savedCallbackUnreliable({ newFileName, newFileFormatID, newFileHandle, newBlob })
(可選):用戶保存文件時調用的函數。 newBlob
應該來自getBlob(newFileFormatID)
。dialogTitle
(可選):保存對話框的標題。在此處注意控件的反轉:JS Paint調用您的systemHooks.showSaveFileDialog
函數,然後調用JS Paint的getBlob
函數。一旦getBlob
解決,您可以調用由JS塗料定義的savedCallbackUnreliable
ploce函數。 (希望我將來能澄清一下。)
另請注意,此功能負責保存文件,而不僅僅是選擇保存位置。如果有用,您可以重複使用systemHooks.writeBlobToHandle
功能。
systemHooks.showOpenFileDialog({ formats })
定義此功能以覆蓋默認的打開對話框。這用於打開圖像和調色板。
參數:
formats
:與systemHooks.showSaveFileDialog
相同請注意,此功能負責加載文件內容,而不僅僅是選擇文件。如果有用,您可以重複使用systemHooks.readBlobFromHandle
函數。
systemHooks.writeBlobToHandle(fileHandle, blob)
定義此功能以告訴JS繪製如何保存文件。
參數:
fileHandle
:系統定義的文件句柄,表示要寫入的文件。blob
:代表要保存的文件內容的Blob
。返回:
false
使用true
Promise
,或者如果不知道該文件是否成功保存,則undefined
,而使用<a href="..." download="...">
。承諾不應拒絕;應通過顯示錯誤消息並返回false
來處理錯誤。 systemHooks.readBlobFromHandle(fileHandle)
定義此功能以告訴JS油漆如何加載文件。
參數:
fileHandle
:系統定義的文件句柄表示要讀取的文件。 systemHooks.setWallpaperTiled(canvas)
定義此功能以告訴JS油漆如何設置牆紙。有關示例,請參見集成集作為牆紙。
參數:
canvas
:帶有圖像的HTMLCanvasElement
將其設置為牆紙。 systemHooks.setWallpaperCentered(canvas)
定義此功能以告訴JS油漆如何設置牆紙。有關示例,請參見集成集作為牆紙。
參數:
canvas
:帶有圖像的HTMLCanvasElement
將其設置為牆紙。 undoable({ name, icon }, actionFunction)
用它使動作變得不可行。
此功能採用畫布和其他一些狀態的快照,然後調用actionFunction
函數。它在歷史上創建了一個條目,因此可以撤消。
參數:
name
:動作的名稱,例如"Brush"
或"Rotate Image 270°"
icon
(可選):要在“歷史記錄”窗口中顯示的Image
。建議使用15x11像素。actionFunction
:不需要參數並修改畫布的函數。 show_error_message(message, [error])
使用它顯示一個錯誤消息對話框,可選地使用可擴展的錯誤詳細信息。
參數:
message
:要在對話框中顯示的純文本。error
(可選):在對話框中顯示的Error
對象,默認情況下在“詳細信息”可擴展部分中倒塌。 open_from_file(blob, source_file_handle)
用它將文件加載到應用程序中。
參數:
blob
:代表要加載的文件的Blob
對象。source_file_handle
:系統定義的文件的相應文件句柄。對不起,古怪的API。 API是新的,部分根本沒有設計。這只是我依靠的一個黑客,進入了JS塗料的內部以加載文件。我決定將其記錄為API的第一個版本,因為無論如何在升級使用時,我想要一個更改。
set_theme(theme_file_name)
用它來更改應用程序的外觀。
參數:
theme_file_name
:要加載的主題文件的名稱,其中之一:"classic.css"
:Windows98主題。"dark.css"
:黑暗主題。"modern.css"
:現代主題。"winter.css"
:節日冬季主題。"occult.css"
:撒旦主題。 set_language(language_code)
您可以使用它來更改應用程序的語言。但是實際上,它將向用戶提示更改語言,因為應用程序需要重新加載以應用更改。而且,如果該對話框不是正確的語言,那麼它們可能會感到困惑。
參數:
language_code
:要使用的語言代碼,例如英語的"en"
,用於傳統中文的"zh"
, "zh-simplified"
,等等。 API會發生很多變化,但更改將在ChangElog中進行記錄。
不僅是變化的歷史,而且是遷移/升級指南。
有關通用項目新聞,請單擊應用程序中的Extras>“項目新聞” 。
JS塗料是免費的開源軟件,並根據MIT允許的MIT許可證許可。