posts's Introduction
posts's People
posts's Issues
canvas drawImage绘图实现contain和cover的效果
我们常用到css的background-size属性中的contain和cover属性,来实现背景图的适配,
本文将要介绍在用canvas绘制图片的时候,如何实现contain或cover效果呢?
contain概念
缩放背景图片以完全装入背景区,可能背景区部分空白。contain 尽可能的缩放背景并保持
图像的宽高比例(图像不会被压缩)。该背景图会填充所在的容器。当背景图和容器的大小
不同时,容器的空白区域(上/下或者左/右)会显示由 background-color 设置的背景颜色。
说白了就是保持图片宽高比并保证图片的长边能完整显示,用到canvas的 drawImage(img, dx, dy, dw, dh)方法
参数dx, dy分别表示图片左上角顶点的横 纵坐标
参数dw, dh分别表示定义图片的宽 高。
公式推导
需要分两种情况,
已知:
imgRatio = imgWidth / imgHeight
canvasRatio = canvasWidth / canvasHeight
由图得出条件:
imgHeight = canvasHeight
推导:
imgWidth = imgRatio * imgHeight = imgRatio * canvasHeight
即:
dw = imgRatio * canvasHeight
dh = canvasHeight
dx = (canvasWidth - imgWidth) / 2
dy = 0
同理推导出:
imgWidth = canvasWidth
imgHeight = imgWidth / imgRatio = canvasWidth / imgRatio
即:
dw = canvasWidth
dh = dw / imgRatio
dx = 0
dy = (canvasHeight - dh) / 2
所以以contain方式适应画布的代码为:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = "images/pic1.jpg";
let dx, dy, dw, dh, imgRatio, canvasRatio;
canvasRatio = canvas.width / canvas.height;
// contain 方式
img.onload = function () {
imgRatio = img.width / img.height;
if(imgRatio <= canvasRatio){
dw = imgRatio * canvas.width
dh = canvas.height
dx = (canvas.width - dw) / 2
dy = 0
}else{
dw = canvas.width
dh = dw / imgRatio
dx = 0
dy = (canvas.height - dh) / 2
}
ctx.drawImage(img, dx, dy, dw, dh)
}
cover概念
缩放背景图片以完全覆盖背景区,可能背景图片部分看不见。和 contain 值相反,
cover 值尽可能大的缩放背景图像并保持图像的宽高比例(图像不会被压扁)。
该背景图以它的全部宽或者高覆盖所在容器。当容器和背景图大小不同时,
背景图的 左/右 或者 上/下 部分会被裁剪。
说白了就是保持图片宽高比的同时保证图片的短边能完全显示出来,
因为可能会有裁剪,所以用到canvas的drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)方法。
参数sx, sy分别表示源图片被截取部分左上角顶点的横 纵坐标
参数sw, sh 分别表示源图片被截取部分的宽 高
参数 dx, dy, dw, dh 分别表示切片后将要在画布上绘制的左上角顶点坐标及绘制的宽高。
公式推导
同样分两种情况,
① 图片宽高比 <= 画布宽高比时, 如图:
已知:
imgRatio = imgWidth / imgHeight
canvasRatio = canvasWidth / canvasHeight
由图知:
dw = canvasWidth
dh = canvasHeight
dx = 0
dy = 0
此时图片的宽要在画布完整展示,所以源图片要裁剪的区域为:
sw = imgWidth
sh = sw / canvasRatio
sx = 0
sy = (imgHeight - sh) / 2
②当图片宽高比 >= 画布宽高比时, 如图:
同理:
此时图片的高要在画布完整展示,源图片的裁剪区域为:
sh = imgHeight
sw = sh * canvasRatio
sx = (imgWidth - sw) / 2
sy = 0
所以以cover方式适应画布的代码为:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = "images/pic1.jpg";
let sx, sy, sw, sh, imgRatio, canvasRatio;
canvasRatio = canvas.width / canvas.height;
// cover 方式
img.onload = function () {
imgRatio = img.width / img.height;
if(imgRatio <= canvasRatio){
sw = img.width
sh = sw / canvasRatio
sx = 0
sy = (img.height - sh) / 2
}else{
sh = img.height
sw = sh * canvasRatio
sx = (img.width - sw) / 2
sy = 0
}
ctx.drawImage(img, sx, sy, sw, sh, 0, 0, canvas.width, canvas.height) // 因为是cover覆盖, 所以dx,dy都是0,绘制宽高即为canvas宽高
}
正则 —— (.*)与(.*?)和一些基础的东西
1. *与?
来看最基础的正则符号 ---- * ? ------
(.*) 是贪婪匹配代表尽可能多的匹配字符因此它将h和l之间所有的字符都匹配了出来
(.*?) 是懒惰匹配尽可能匹配少的字符但是要匹配出所有的字符
(.??)这个是匹配重复一次和零次的
其中的数量我们也是可以自己定义的
.{n,m}?代表匹配数量从n到m数量的字符
2. 正则的基础语法
对于 ^[0-9a-zA-Z_-]+abcdef$
我们怎么来解释呢?
^为匹配输入字符串的开始位置。
- [0-9]+匹配多个数字, [0-9] 匹配单个数字,+ 匹配一个或者多个。.
- abc$匹配字母 abc 并以 abc 结尾,$ 为匹配输入字符串的结束位置。
- [0-9a-zA-Z_-]代表由数字小写字母大写字母下划线终止符组成的加号代表由多个字符组成的
- abcdef$匹配字母 abcdef 并以 abcdef 结尾,$ 为匹配输入字符串的结束位置。
友链
神代绯樱
link: http://blog.kotomi.cc/
cover: https://avatars.githubusercontent.com/u/48148944?s=88&u=5afac6666d57b3374efd63daefce7cba4bb80ab0&v=4
description: 方不及未哀,咫尺尽头,始见空音
ffmpeg相关配置
视频合并
ffmpeg -f concat -i filelist.txt -c copy output_set.mp4
filelist.txt 为视频文件列表 需为如下格式
file '0.mp4'
file '1.mp4'
file '2.mp4'
file '3.mp4'
file '4.mp4'
cmd命令 dir /b/a-d/on > filelist.txt
获取当前目录下的文件名
视频音频合并
ffmpeg -i 1.mp4 -i 1.aac -vcodec copy -acodec copy output.mp4
视频切割 ss 开始时间 t持续时间 to结束时间
ffmpeg -y -i ccc.mkv -ss 00:00:01 -to 00:01:32 -c:v libx264 -an ccc.mp4
图片合成视频,单张图片
ffmpeg -loop 1 -i img.jpg -c:v libx264 -t 30 -pix_fmt yuv420p out.mp4
图片合成视频
ffmpeg -y -threads 2 -r 10 -t 10 -i %d.png -vcodec libx264 -crf 30 out.mkv
视频遮罩过渡
-an 不生成音频 -vn 不生成视频
2种方法实现偏移 在-i 文件名.mp4 前加上偏移量 -itsoffset 13
ffmpeg -y -i 111.mp4 -itsoffset 13 -i transition.mp4 -itsoffset 13 -i ccc.mp4 -filter_complex "[0]format=yuva420p[fg];[2][1]alphamerge[frame];[fg][frame]overlay[mask];[mask]concat=n=1:v=1:a=1[v][a]" -map "[v]" -map "[a]" out.mkv
通过setpts 实现偏移 [2]setpts=PTS+13/TB[f1];[2]asetpts=PTS+13/TB[f1a]
偏移10秒 setpts=PTS+10/TB
ffmpeg -y -i 111.mp4 -i transition.mp4 -i ccc.mp4 -filter_complex "[0]format=yuva420p[fg];[2]setpts=PTS+13/TB[f1];[2]asetpts=PTS+13/TB[f1a];[1]setpts=PTS+13/TB[f2];[f1][f2]alphamerge[frame];[fg][frame]overlay[mask];[mask][f1a]concat=n=1:v=1:a=1[v][a]" -map "[v]" -map "[a]" out.mkv
视频拆分合并
拆分成2个视频并合并 setpts 更改时间线 光select会导致时间线过长问题
N 视频的输入帧计数或消耗的样本数,不包括音频的当前帧,从 0 开始。
FRAME_RATE 帧率,仅为恒定帧率视频定义。
TB 输入时间戳的时基。
ffmpeg -y -i ccc.mp4 -filter_complex "[0]split=2[a][b];[a]select='lt(t\,13)',setpts=N/FRAME_RATE/TB[A];[b]select='gte(t\,60)',setpts=N/FRAME_RATE/TB[B];[A][B]concat=n=2:v=1:a=0[v]" -filter_complex "[0]asplit=2[a1][b1];[a1]aselect='lt(t\,13)',asetpts=N/SR/TB[A1];[b1]aselect='gte(t\,60)',asetpts=N/SR/TB[B1];[A1][B1]concat=n=2:v=0:a=1[a]" -map "[a]" -map "[v]" -vcodec libx264 -acodec aac out.mkv
多张图片生成视频
ffmpeg -y -loop 1 -t 21 -r 60 -i 1.png -filter_complex "movie=2.png:loop=1[out1];movie=3.png:loop=1[out2];[0][out1]overlay=enable='gt(t, 11)'[final1];[final1][out2]overlay=enable='gt(t, 16)'" -vcodec libx264 -acodec aac -pix_fmt yuv420p png.mkv
chainWebpack常用配置方式
chainWebpack常用配置集合
webpack 配置很多,这里我们探讨比较经常需要修改的:
-
不复杂,可以在 configWebpack 中操作:
- mode
- devtool
-
配置复杂,可以在 chainWebpack 中操作:
- module.rules
- plugins
- optimization
1 输入输出配置
module.exports = {
chainWebpack: config => {
// 清理所有默认入口配置
config.entryPoints.clear();
// 增加一个入口main
config.entry("main").add("./src/main.js");
// 增加一个入口about
config.entry("about").add("./src/about.js");
config.output
.path("dist")//输出目录
.filename("[name].[chunkhash].js")//输出文件名
.chunkFilename("chunks/[name].[chunkhash].js")//输出chunk名
.libraryTarget("umd")//输出格式
.library();//输出库
}
};
// 其他的output配置
config.output
.auxiliaryComment(auxiliaryComment)
.chunkFilename(chunkFilename)
.chunkLoadTimeout(chunkLoadTimeout)
.crossOriginLoading(crossOriginLoading)
.devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate)
.devtoolLineToLine(devtoolLineToLine)
.devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate)
.filename(filename)
.hashFunction(hashFunction)
.hashDigest(hashDigest)
.hashDigestLength(hashDigestLength)
.hashSalt(hashSalt)
.hotUpdateChunkFilename(hotUpdateChunkFilename)
.hotUpdateFunction(hotUpdateFunction)
.hotUpdateMainFilename(hotUpdateMainFilename)
.jsonpFunction(jsonpFunction)
.library(library)
.libraryExport(libraryExport)
.libraryTarget(libraryTarget)
.path(path)
.pathinfo(pathinfo)
.publicPath(publicPath)
.sourceMapFilename(sourceMapFilename)
.sourcePrefix(sourcePrefix)
.strictModuleExceptionHandling(strictModuleExceptionHandling)
.umdNamedDefine(umdNamedDefine)
2.设置别名
const path = require("path");
function resolve(dir) {
return path.join(__dirname, dir);
}
module.exports = {
chainWebpack: config => {
config.resolve.alias
.set("@$", resolve("src"))
.set("assets", resolve("src/assets"))
.set("components", resolve("src/components"))
.set("layout", resolve("src/layout"))
.set("base", resolve("src/base"))
.set("static", resolve("src/static"))
.delete("base"); // 删掉指定的别名
// .clear() 会把全部别名都删掉
}
};
3. 配置代理
odule.exports = {
chainWebpack: config => {
config.devServer
.port("8080")
.open(true)
.proxy({
"/api": {
target: "http://www.baidu.com",
changeOrigin: true,
pathRewrite: {
"^/api": ""
}
}
});
}
};
// chain其余proxy的配置
config.devServer
.bonjour(bonjour)
.clientLogLevel(clientLogLevel)
.color(color)
.compress(compress)
.contentBase(contentBase)
.disableHostCheck(disableHostCheck)
.filename(filename)
.headers(headers)
.historyApiFallback(historyApiFallback)
.host(host)
.hot(hot)
.hotOnly(hotOnly)
.https(https)
.inline(inline)
.info(info)
.lazy(lazy)
.noInfo(noInfo)
.open(open)
.openPage(openPage)
.overlay(overlay)
.pfx(pfx)
.pfxPassphrase(pfxPassphrase)
.port(port)
.progress(progress)
.proxy(proxy)
.public(public)
.publicPath(publicPath)
.quiet(quiet)
.setup(setup)
.socket(socket)
.staticOptions(staticOptions)
.stats(stats)
.stdin(stdin)
.useLocalIp(useLocalIp)
.watchContentBase(watchContentBase)
.watchOptions(watchOptions)
5. 使用插件和添加、删除插件参数
// 添加API
config
.plugin(name)
.use(WebpackPlugin, args)
// 一个例子
const fileManager = require("filemanager-webpack-plugin");
...
//注意:use部分,不能使用new的方式建立插件实例
webpackConfig.plugin("zip").use(fileManager, [
{
onEnd: {
archive: [
{
source: "dist",
destination: zipName
}
]
}
}
]);
修改参数、添加参数
module.exports = {
chainWebpack: config => {
// 可使用tap方式,修改插件参数
config.plugin(name).tap(args => newArgs);
// 一个例子
config
.plugin("env")
//使用tag修改参数
.tap(args => [...args, "SECRET_KEY"]);
//更改html插件的title
config.plugin("html").tap(args => {
args[0] = "documnet的title";
return args[0];
});
}
};
6.修改插件初始化和删除插件
6.1 修改插件
module.exports = {
chainWebpack: config => {
config.plugin(name)
.init((Plugin, args) => new Plugin(...args));
}
};
6.2删除插件
module.exports = {
chainWebpack: config => {
config.plugins.delete("prefetch");
// 移除 preload 插件
config.plugins.delete("preload");
}
};
7.有前后顺序的插件
// A插件之前要调用B插件;
config
.plugin(name)
.before(otherName)
module.exports = {
chainWebpack: config => {
config
.plugin("b")
.use(B)
.end()
.plugin("a")
.use(A)
.before(B);
}
};
// A插件之后要调用B插件;
config
.plugin(name)
.after(otherName);
module.exports = {
chainWebpack: config => {
config
.plugin("a")
.after("b")
.use(A)
.end()
.plugin("b")
.use(B);
}
}
performace性能警告阀配置
config.performance
//false | "error" | "warning"。打开/关闭提示
.hints(hints)
//入口起点表示针对指定的入口,对于全部资源,要充分利用初始加载时(initial load time)期间。此选项根据入口起点的最大致积,控制 webpack 什么时候生成性能提示。默认值是:250000
.maxEntrypointSize(maxEntrypointSize)
//资源(asset)是从 webpack 生成的任何文件。此选项根据单个资源体积,控制 webpack 什么时候生成性能提示。默认值是:250000
.maxAssetSize(maxAssetSize)
//此属性容许 webpack 控制用于计算性能提示的文件
.assetFilter(assetFilter)
使用、修改、删除loader
//使用loader
module.exports = {
chainWebpack: config => {
config.module
.rule(name)
.use(name)
.loader(loader)
.options();
}};
//修改参数
module.exports = {
chainWebpack: config => {
config.module
.rule(name)
.use(name)
.loader(loader)
.tap(options => {
let newOptions = { ...options, xx: "黑黑" };
return newOptions;
});
}};
// 覆盖原来的loader
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule("svg");
// 清除已有的全部 loader。
// 若是你不这样作,接下来的 loader 会附加在该规则现有的 loader 以后。
svgRule.uses.clear();
svgRule
.use("vue-svg-loader")
.loader("vue-svg-loader");
}};
//使原来loader忽略某个目录,使用新的loader
module.exports = {
chainWebpack: config => {
config.module
.rule("svg")
.exclude.add(resolve("src/icons"))
.end();
config.module
.rule("icons")
.test("/.svg$/")
.include.add(resolve("src/icons"))
.end()
.use("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end();
}};
条件
config.when(process.env.NODE_ENV !== "development",
config => {
config
.plugin("ScriptExtHtmlWebpackPlugin")
.after("html")
.use("script-ext-html-webpack-plugin", [
{
// `runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}
])
.end();
config.optimization.splitChunks({
chunks: "all",
cacheGroups: {
libs: {
name: "chunk-libs",
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: "initial"
// only package third parties that are initially dependent
},
elementUI: {
name: "chunk-elementUI",
// split elementUI into a single package
priority: 20,
// the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]_?element-ui(.*)
/ // in order to adapt to cnpm
},
commons: {
name: "chunk-commons",
test: resolve("src/components"),
// can customize your rules
minChunks: 3,
// minimum common number
priority: 5,
reuseExistingChunk: true
}
}
});
// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
config.optimization.runtimeChunk("single");
});
删除单个规则中的全部 loader
// vue.config.js
module.exports = {
chainWebpack: config => {
const svgRule = config.module.rule('svg')
// 清除已有规则 svg 的所有 loader。
// 如果你不这样做,接下来的 loader 会附加在该规则现有的 loader 之后。
svgRule.uses.clear()
// 添加要替换的 loader
svgRule
.use('vue-svg-loader')
.loader('vue-svg-loader')
}}
删除单个规则中的一个loader
// 删除前:
{
test: /\.m?jsx?$/,
exclude: [
function () {
/* omitted long function */
}
],
use: [
/* config.module.rule('js').use('cache-loader') */
{
loader: 'cache-loader',
options: {
cacheDirectory: 'D:\\webproject\\webapp-jy\\node_modules\\.cache\\babel-loader',
cacheIdentifier: '519fc596'
}
},
/* config.module.rule('js').use('babel-loader') */
{
loader: 'babel-loader'
}
]
}
// 删除 cache-loader
const jsRule = config.module.rule('js')
jsRule.uses.delete('cache-loader')
// 删除后:
{
test: /\.m?jsx?$/,
exclude: [
function () { /* omitted long function */ }
],
use: [
/* config.module.rule('js').use('babel-loader') */
{
loader: 'babel-loader'
}
]
}
删除单个规则
const moduleRule = config.module
// 删除命名为 js 的规则
moduleRule.rules.delete('js')
删除插件
config.plugins.delete(name)
删除 optimization.minimizers
config.optimization.minimizers.delete(name)
增加规则
// loader 默认是从下往上处理
// enforce: 决定现有规则调用顺序
// - pre 优先处理
// - normal 正常处理(默认)
// - inline 其次处理
// - post 最后处理
module.exports = {
chainWebpack: config => {
config.module
.rule('lint')
// 定义一个名叫 lint 的规则
.test(/\.js$/)
// 设置 lint 的匹配正则
.pre()
// 指定当前规则的调用优先级
.include
// 设置当前规则的作用目录,只在当前目录下才执行当前规则
.add('src')
.end()
.use('eslint')
// 指定一个名叫 eslint 的 loader 配置
.loader('eslint-loader')
// 该配置使用 eslint-loader 作为处理 loader
.options({
// 该 eslint-loader 的配置
rules: {
semi: 'off'
}
})
.end()
.use('zidingyi')
// 指定一个名叫 zidingyi 的 loader 配置
.loader('zidingyi-loader')
// 该配置使用 zidingyi-loader 作为处理 loader
.options({ // 该 zidingyi-loader 的配置
rules: {
semi: 'off'
}
})
config.module
.rule('compile')
.test(/\.js$/)
.include
.add('src')
.add('test')
.end()
.use('babel')
.loader('babel-loader')
.options({
presets: [
['@babel/preset-env', { modules: false }]
]
})
}}
最后将解析为如下配置
{
module: {
rules: [
/* config.module.rule('lint') */
{
test: /\.js$/,
enforce: 'pre',
include: ['src'],
use: [
/* config.module.rule('lint').use('eslint') */
{
loader: 'eslint-loader',
options: {
rules: {
semi: 'off'
}
}
},
/* config.module.rule('lint').use('zidingyi') */
{
loader: 'zidingyi-loader',
options: {
rules: {
semi: 'off'
}
}
}
]
},
/* config.module.rule('compile') */
{
test: /\.js$/,
include: ['src', 'test'],
use: [
/* config.module.rule('compile').use('babel') */
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { modules: false }]
]
}
}
]
}
]
}
}
splitChunks配置
config.when(process.env.NODE_ENV!== 'development',config=>{
config.optimization.splitChunks({
chunks: "all", // 共有3个值"initial","async"和"all"。配置后,代码分割优化仅选择初始块,按需块或所有块
minSize: 30000, // (默认值:30000)块的最小大小
minChunks: 1, // (默认值:1)在拆分之前共享模块的最小块数
maxAsyncRequests: 5, //(默认为5)按需加载时并行请求的最大数量
maxInitialRequests: 3, //(默认值为3)入口点的最大并行请求数
automaticNameDelimiter: '~', // 默认情况下,webpack将使用块的来源和名称生成名称,例如vendors~main.js
name: true,
cacheGroups: { // 以上条件都满足后会走入cacheGroups进一步进行优化的判断
vendors: {
test: /[\\/]node_modules[\\/]/, // 判断引入库是否是node_modules里的
priority: -10, // 数字越大优先级越高 (-10大于-20)
filename: 'vendors.js' // 设置代码分割后的文件名
},
default: { //所有代码分割快都符合默认值,此时判断priority优先级
minChunks: 2,
priority: -20,
reuseExistingChunk: true // 允许在模块完全匹配时重用现有的块,而不是创建新的块。
}
}
})
})
test
Hello world
愚者
沉默过往的愚者
项目
npm相关
npm command参数带入
如果执行npm run [command] 将附加参数带入到command里进行执行需要加--
如:
npm run build -- --a=1 --b=2
输出:
cross-env UMI_ENV=prod umi build --a=1 --b=2
antd遇到的问题记录
Select点击选项没有被选中
检查options的value是否为null
Radio.Group点击选项没有被选中
检查onChange的赋值,参数为RadioChangeEvent,需要取event.target.value
关于
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.